@code-pushup/cli 0.48.0 → 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { hideBin } from "yargs/helpers";
|
|
5
5
|
|
|
6
6
|
// packages/cli/src/lib/autorun/autorun-command.ts
|
|
7
|
-
import
|
|
7
|
+
import { bold as bold7, gray as gray4 } from "ansis";
|
|
8
8
|
|
|
9
9
|
// packages/models/src/lib/implementation/schemas.ts
|
|
10
10
|
import { MATERIAL_ICONS } from "vscode-material-icons";
|
|
@@ -756,11 +756,262 @@ function comparePairs(pairs, equalsFn) {
|
|
|
756
756
|
import { spawn } from "node:child_process";
|
|
757
757
|
|
|
758
758
|
// packages/utils/src/lib/reports/utils.ts
|
|
759
|
-
import
|
|
759
|
+
import ansis from "ansis";
|
|
760
|
+
import { md } from "build-md";
|
|
761
|
+
|
|
762
|
+
// packages/utils/src/lib/reports/constants.ts
|
|
763
|
+
var TERMINAL_WIDTH = 80;
|
|
764
|
+
var SCORE_COLOR_RANGE = {
|
|
765
|
+
GREEN_MIN: 0.9,
|
|
766
|
+
YELLOW_MIN: 0.5
|
|
767
|
+
};
|
|
768
|
+
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
769
|
+
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
770
|
+
var README_LINK = "https://github.com/code-pushup/cli#readme";
|
|
771
|
+
var REPORT_HEADLINE_TEXT = "Code PushUp Report";
|
|
772
|
+
var REPORT_RAW_OVERVIEW_TABLE_HEADERS = [
|
|
773
|
+
"Category",
|
|
774
|
+
"Score",
|
|
775
|
+
"Audits"
|
|
776
|
+
];
|
|
777
|
+
|
|
778
|
+
// packages/utils/src/lib/reports/utils.ts
|
|
779
|
+
function formatReportScore(score) {
|
|
780
|
+
const scaledScore = score * 100;
|
|
781
|
+
const roundedScore = Math.round(scaledScore);
|
|
782
|
+
return roundedScore === 100 && score !== 1 ? Math.floor(scaledScore).toString() : roundedScore.toString();
|
|
783
|
+
}
|
|
784
|
+
function formatScoreWithColor(score, options2) {
|
|
785
|
+
const styledNumber = options2?.skipBold ? formatReportScore(score) : md.bold(formatReportScore(score));
|
|
786
|
+
return md`${scoreMarker(score)} ${styledNumber}`;
|
|
787
|
+
}
|
|
788
|
+
var MARKERS = {
|
|
789
|
+
circle: {
|
|
790
|
+
red: "\u{1F534}",
|
|
791
|
+
yellow: "\u{1F7E1}",
|
|
792
|
+
green: "\u{1F7E2}"
|
|
793
|
+
},
|
|
794
|
+
square: {
|
|
795
|
+
red: "\u{1F7E5}",
|
|
796
|
+
yellow: "\u{1F7E8}",
|
|
797
|
+
green: "\u{1F7E9}"
|
|
798
|
+
}
|
|
799
|
+
};
|
|
800
|
+
function scoreMarker(score, markerType = "circle") {
|
|
801
|
+
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
802
|
+
return MARKERS[markerType].green;
|
|
803
|
+
}
|
|
804
|
+
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
805
|
+
return MARKERS[markerType].yellow;
|
|
806
|
+
}
|
|
807
|
+
return MARKERS[markerType].red;
|
|
808
|
+
}
|
|
809
|
+
function getDiffMarker(diff) {
|
|
810
|
+
if (diff > 0) {
|
|
811
|
+
return "\u2191";
|
|
812
|
+
}
|
|
813
|
+
if (diff < 0) {
|
|
814
|
+
return "\u2193";
|
|
815
|
+
}
|
|
816
|
+
return "";
|
|
817
|
+
}
|
|
818
|
+
function colorByScoreDiff(text, diff) {
|
|
819
|
+
const color = diff > 0 ? "green" : diff < 0 ? "red" : "gray";
|
|
820
|
+
return shieldsBadge(text, color);
|
|
821
|
+
}
|
|
822
|
+
function shieldsBadge(text, color) {
|
|
823
|
+
return md.image(
|
|
824
|
+
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
825
|
+
text
|
|
826
|
+
);
|
|
827
|
+
}
|
|
828
|
+
function formatDiffNumber(diff) {
|
|
829
|
+
const number = Math.abs(diff) === Number.POSITIVE_INFINITY ? "\u221E" : `${Math.abs(diff)}`;
|
|
830
|
+
const sign = diff < 0 ? "\u2212" : "+";
|
|
831
|
+
return `${sign}${number}`;
|
|
832
|
+
}
|
|
833
|
+
function severityMarker(severity) {
|
|
834
|
+
if (severity === "error") {
|
|
835
|
+
return "\u{1F6A8}";
|
|
836
|
+
}
|
|
837
|
+
if (severity === "warning") {
|
|
838
|
+
return "\u26A0\uFE0F";
|
|
839
|
+
}
|
|
840
|
+
return "\u2139\uFE0F";
|
|
841
|
+
}
|
|
842
|
+
function formatScoreChange(diff) {
|
|
843
|
+
const marker = getDiffMarker(diff);
|
|
844
|
+
const text = formatDiffNumber(Math.round(diff * 1e3) / 10);
|
|
845
|
+
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
846
|
+
}
|
|
847
|
+
function formatValueChange({
|
|
848
|
+
values,
|
|
849
|
+
scores
|
|
850
|
+
}) {
|
|
851
|
+
const marker = getDiffMarker(values.diff);
|
|
852
|
+
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
|
|
853
|
+
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
854
|
+
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
855
|
+
}
|
|
856
|
+
function calcDuration(start, stop) {
|
|
857
|
+
return Math.round((stop ?? performance.now()) - start);
|
|
858
|
+
}
|
|
859
|
+
function countCategoryAudits(refs, plugins) {
|
|
860
|
+
const groupLookup = plugins.reduce(
|
|
861
|
+
(lookup, plugin) => {
|
|
862
|
+
if (plugin.groups == null || plugin.groups.length === 0) {
|
|
863
|
+
return lookup;
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
...lookup,
|
|
867
|
+
[plugin.slug]: Object.fromEntries(
|
|
868
|
+
plugin.groups.map((group) => [group.slug, group])
|
|
869
|
+
)
|
|
870
|
+
};
|
|
871
|
+
},
|
|
872
|
+
{}
|
|
873
|
+
);
|
|
874
|
+
return refs.reduce((acc, ref) => {
|
|
875
|
+
if (ref.type === "group") {
|
|
876
|
+
const groupRefs = groupLookup[ref.plugin]?.[ref.slug]?.refs;
|
|
877
|
+
return acc + (groupRefs?.length ?? 0);
|
|
878
|
+
}
|
|
879
|
+
return acc + 1;
|
|
880
|
+
}, 0);
|
|
881
|
+
}
|
|
882
|
+
function compareCategoryAuditsAndGroups(a, b) {
|
|
883
|
+
if (a.weight !== b.weight) {
|
|
884
|
+
return b.weight - a.weight;
|
|
885
|
+
}
|
|
886
|
+
if (a.score !== b.score) {
|
|
887
|
+
return a.score - b.score;
|
|
888
|
+
}
|
|
889
|
+
if ("value" in a && "value" in b && a.value !== b.value) {
|
|
890
|
+
return b.value - a.value;
|
|
891
|
+
}
|
|
892
|
+
return a.title.localeCompare(b.title);
|
|
893
|
+
}
|
|
894
|
+
function compareAudits(a, b) {
|
|
895
|
+
if (a.score !== b.score) {
|
|
896
|
+
return a.score - b.score;
|
|
897
|
+
}
|
|
898
|
+
if (a.value !== b.value) {
|
|
899
|
+
return b.value - a.value;
|
|
900
|
+
}
|
|
901
|
+
return a.title.localeCompare(b.title);
|
|
902
|
+
}
|
|
903
|
+
function compareIssueSeverity(severity1, severity2) {
|
|
904
|
+
const levels = {
|
|
905
|
+
info: 0,
|
|
906
|
+
warning: 1,
|
|
907
|
+
error: 2
|
|
908
|
+
};
|
|
909
|
+
return levels[severity1] - levels[severity2];
|
|
910
|
+
}
|
|
911
|
+
function throwIsNotPresentError(itemName, presentPlace) {
|
|
912
|
+
throw new Error(`${itemName} is not present in ${presentPlace}`);
|
|
913
|
+
}
|
|
914
|
+
function getPluginNameFromSlug(slug, plugins) {
|
|
915
|
+
return plugins.find(({ slug: pluginSlug }) => pluginSlug === slug)?.title || slug;
|
|
916
|
+
}
|
|
917
|
+
function compareIssues(a, b) {
|
|
918
|
+
if (a.severity !== b.severity) {
|
|
919
|
+
return -compareIssueSeverity(a.severity, b.severity);
|
|
920
|
+
}
|
|
921
|
+
if (!a.source && b.source) {
|
|
922
|
+
return -1;
|
|
923
|
+
}
|
|
924
|
+
if (a.source && !b.source) {
|
|
925
|
+
return 1;
|
|
926
|
+
}
|
|
927
|
+
if (a.source?.file !== b.source?.file) {
|
|
928
|
+
return a.source?.file.localeCompare(b.source?.file || "") ?? 0;
|
|
929
|
+
}
|
|
930
|
+
if (!a.source?.position && b.source?.position) {
|
|
931
|
+
return -1;
|
|
932
|
+
}
|
|
933
|
+
if (a.source?.position && !b.source?.position) {
|
|
934
|
+
return 1;
|
|
935
|
+
}
|
|
936
|
+
if (a.source?.position?.startLine !== b.source?.position?.startLine) {
|
|
937
|
+
return (a.source?.position?.startLine ?? 0) - (b.source?.position?.startLine ?? 0);
|
|
938
|
+
}
|
|
939
|
+
return 0;
|
|
940
|
+
}
|
|
941
|
+
function applyScoreColor({ score, text }, style = ansis) {
|
|
942
|
+
const formattedScore = text ?? formatReportScore(score);
|
|
943
|
+
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
944
|
+
return text ? style.green(formattedScore) : style.bold(style.green(formattedScore));
|
|
945
|
+
}
|
|
946
|
+
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
947
|
+
return text ? style.yellow(formattedScore) : style.bold(style.yellow(formattedScore));
|
|
948
|
+
}
|
|
949
|
+
return text ? style.red(formattedScore) : style.bold(style.red(formattedScore));
|
|
950
|
+
}
|
|
951
|
+
function targetScoreIcon(score, targetScore, options2 = {}) {
|
|
952
|
+
if (targetScore != null) {
|
|
953
|
+
const {
|
|
954
|
+
passIcon = "\u2705",
|
|
955
|
+
failIcon = "\u274C",
|
|
956
|
+
prefix = "",
|
|
957
|
+
postfix = ""
|
|
958
|
+
} = options2;
|
|
959
|
+
if (score >= targetScore) {
|
|
960
|
+
return `${prefix}${passIcon}${postfix}`;
|
|
961
|
+
}
|
|
962
|
+
return `${prefix}${failIcon}${postfix}`;
|
|
963
|
+
}
|
|
964
|
+
return "";
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
// packages/utils/src/lib/execute-process.ts
|
|
968
|
+
var ProcessError = class extends Error {
|
|
969
|
+
code;
|
|
970
|
+
stderr;
|
|
971
|
+
stdout;
|
|
972
|
+
constructor(result) {
|
|
973
|
+
super(result.stderr);
|
|
974
|
+
this.code = result.code;
|
|
975
|
+
this.stderr = result.stderr;
|
|
976
|
+
this.stdout = result.stdout;
|
|
977
|
+
}
|
|
978
|
+
};
|
|
979
|
+
function executeProcess(cfg) {
|
|
980
|
+
const { observer, cwd, command: command2, args, ignoreExitCode = false } = cfg;
|
|
981
|
+
const { onStdout, onError, onComplete } = observer ?? {};
|
|
982
|
+
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
983
|
+
const start = performance.now();
|
|
984
|
+
return new Promise((resolve, reject) => {
|
|
985
|
+
const process2 = spawn(command2, args, { cwd, shell: true });
|
|
986
|
+
let stdout = "";
|
|
987
|
+
let stderr = "";
|
|
988
|
+
process2.stdout.on("data", (data) => {
|
|
989
|
+
stdout += String(data);
|
|
990
|
+
onStdout?.(String(data));
|
|
991
|
+
});
|
|
992
|
+
process2.stderr.on("data", (data) => {
|
|
993
|
+
stderr += String(data);
|
|
994
|
+
});
|
|
995
|
+
process2.on("error", (err) => {
|
|
996
|
+
stderr += err.toString();
|
|
997
|
+
});
|
|
998
|
+
process2.on("close", (code2) => {
|
|
999
|
+
const timings = { date, duration: calcDuration(start) };
|
|
1000
|
+
if (code2 === 0 || ignoreExitCode) {
|
|
1001
|
+
onComplete?.();
|
|
1002
|
+
resolve({ code: code2, stdout, stderr, ...timings });
|
|
1003
|
+
} else {
|
|
1004
|
+
const errorMsg = new ProcessError({ code: code2, stdout, stderr, ...timings });
|
|
1005
|
+
onError?.(errorMsg);
|
|
1006
|
+
reject(errorMsg);
|
|
1007
|
+
}
|
|
1008
|
+
});
|
|
1009
|
+
});
|
|
1010
|
+
}
|
|
760
1011
|
|
|
761
1012
|
// packages/utils/src/lib/file-system.ts
|
|
1013
|
+
import { bold, gray } from "ansis";
|
|
762
1014
|
import { bundleRequire } from "bundle-require";
|
|
763
|
-
import chalk2 from "chalk";
|
|
764
1015
|
import { mkdir, readFile, readdir, rm, stat } from "node:fs/promises";
|
|
765
1016
|
|
|
766
1017
|
// packages/utils/src/lib/formatting.ts
|
|
@@ -823,55 +1074,7 @@ function isPromiseRejectedResult(result) {
|
|
|
823
1074
|
// packages/utils/src/lib/logging.ts
|
|
824
1075
|
import isaacs_cliui from "@isaacs/cliui";
|
|
825
1076
|
import { cliui } from "@poppinss/cliui";
|
|
826
|
-
import
|
|
827
|
-
|
|
828
|
-
// packages/utils/src/lib/reports/constants.ts
|
|
829
|
-
var TERMINAL_WIDTH = 80;
|
|
830
|
-
var SCORE_COLOR_RANGE = {
|
|
831
|
-
GREEN_MIN: 0.9,
|
|
832
|
-
YELLOW_MIN: 0.5
|
|
833
|
-
};
|
|
834
|
-
var CATEGORIES_TITLE = "\u{1F3F7} Categories";
|
|
835
|
-
var FOOTER_PREFIX = "Made with \u2764 by";
|
|
836
|
-
var CODE_PUSHUP_DOMAIN = "code-pushup.dev";
|
|
837
|
-
var README_LINK = "https://github.com/code-pushup/cli#readme";
|
|
838
|
-
var reportHeadlineText = "Code PushUp Report";
|
|
839
|
-
var reportOverviewTableHeaders = [
|
|
840
|
-
{
|
|
841
|
-
key: "category",
|
|
842
|
-
label: "\u{1F3F7} Category",
|
|
843
|
-
align: "left"
|
|
844
|
-
},
|
|
845
|
-
{
|
|
846
|
-
key: "score",
|
|
847
|
-
label: "\u2B50 Score"
|
|
848
|
-
},
|
|
849
|
-
{
|
|
850
|
-
key: "audits",
|
|
851
|
-
label: "\u{1F6E1} Audits"
|
|
852
|
-
}
|
|
853
|
-
];
|
|
854
|
-
var reportRawOverviewTableHeaders = ["Category", "Score", "Audits"];
|
|
855
|
-
var issuesTableHeadings = [
|
|
856
|
-
{
|
|
857
|
-
key: "severity",
|
|
858
|
-
label: "Severity"
|
|
859
|
-
},
|
|
860
|
-
{
|
|
861
|
-
key: "message",
|
|
862
|
-
label: "Message"
|
|
863
|
-
},
|
|
864
|
-
{
|
|
865
|
-
key: "file",
|
|
866
|
-
label: "Source file"
|
|
867
|
-
},
|
|
868
|
-
{
|
|
869
|
-
key: "line",
|
|
870
|
-
label: "Line(s)"
|
|
871
|
-
}
|
|
872
|
-
];
|
|
873
|
-
|
|
874
|
-
// packages/utils/src/lib/logging.ts
|
|
1077
|
+
import { underline } from "ansis";
|
|
875
1078
|
var singletonUiInstance;
|
|
876
1079
|
function ui() {
|
|
877
1080
|
if (singletonUiInstance === void 0) {
|
|
@@ -895,7 +1098,7 @@ function logListItem(args) {
|
|
|
895
1098
|
singletonUiInstance?.logger.log(content);
|
|
896
1099
|
}
|
|
897
1100
|
function link(text) {
|
|
898
|
-
return
|
|
1101
|
+
return underline.blueBright(text);
|
|
899
1102
|
}
|
|
900
1103
|
|
|
901
1104
|
// packages/utils/src/lib/log-results.ts
|
|
@@ -970,10 +1173,10 @@ async function ensureDirectoryExists(baseDir) {
|
|
|
970
1173
|
function logMultipleFileResults(fileResults, messagePrefix) {
|
|
971
1174
|
const succeededTransform = (result) => {
|
|
972
1175
|
const [fileName, size] = result.value;
|
|
973
|
-
const formattedSize = size ? ` (${
|
|
974
|
-
return `- ${
|
|
1176
|
+
const formattedSize = size ? ` (${gray(formatBytes(size))})` : "";
|
|
1177
|
+
return `- ${bold(fileName)}${formattedSize}`;
|
|
975
1178
|
};
|
|
976
|
-
const failedTransform = (result) => `- ${
|
|
1179
|
+
const failedTransform = (result) => `- ${bold(result.reason)}`;
|
|
977
1180
|
logMultipleResults(
|
|
978
1181
|
fileResults,
|
|
979
1182
|
messagePrefix,
|
|
@@ -982,570 +1185,11 @@ function logMultipleFileResults(fileResults, messagePrefix) {
|
|
|
982
1185
|
);
|
|
983
1186
|
}
|
|
984
1187
|
async function importModule(options2) {
|
|
985
|
-
const { mod } = await bundleRequire(options2);
|
|
986
|
-
if (typeof mod === "object" && "default" in mod) {
|
|
987
|
-
return mod.default;
|
|
988
|
-
}
|
|
989
|
-
return mod;
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
// packages/utils/src/lib/text-formats/constants.ts
|
|
993
|
-
var NEW_LINE = "\n";
|
|
994
|
-
var TAB = " ";
|
|
995
|
-
var SPACE = " ";
|
|
996
|
-
|
|
997
|
-
// packages/utils/src/lib/text-formats/html/details.ts
|
|
998
|
-
function details(title, content, cfg = { open: false }) {
|
|
999
|
-
return `<details${cfg.open ? " open" : ""}>${NEW_LINE}<summary>${title}</summary>${NEW_LINE}${// ⚠️ The blank line is needed to ensure Markdown in content is rendered correctly.
|
|
1000
|
-
NEW_LINE}${content}${NEW_LINE}${// @TODO in the future we could consider adding it only if the content ends with a code block
|
|
1001
|
-
// ⚠️ The blank line ensure Markdown in content is rendered correctly.
|
|
1002
|
-
NEW_LINE}</details>${// ⚠️ The blank line is needed to ensure Markdown after details is rendered correctly.
|
|
1003
|
-
NEW_LINE}`;
|
|
1004
|
-
}
|
|
1005
|
-
|
|
1006
|
-
// packages/utils/src/lib/text-formats/html/font-style.ts
|
|
1007
|
-
var boldElement = "b";
|
|
1008
|
-
function bold(text) {
|
|
1009
|
-
return `<${boldElement}>${text}</${boldElement}>`;
|
|
1010
|
-
}
|
|
1011
|
-
var italicElement = "i";
|
|
1012
|
-
function italic(text) {
|
|
1013
|
-
return `<${italicElement}>${text}</${italicElement}>`;
|
|
1014
|
-
}
|
|
1015
|
-
var codeElement = "code";
|
|
1016
|
-
function code(text) {
|
|
1017
|
-
return `<${codeElement}>${text}</${codeElement}>`;
|
|
1018
|
-
}
|
|
1019
|
-
|
|
1020
|
-
// packages/utils/src/lib/text-formats/html/link.ts
|
|
1021
|
-
function link2(href, text) {
|
|
1022
|
-
return `<a href="${href}">${text || href}</a>`;
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
// packages/utils/src/lib/transform.ts
|
|
1026
|
-
function toArray(val) {
|
|
1027
|
-
return Array.isArray(val) ? val : [val];
|
|
1028
|
-
}
|
|
1029
|
-
function objectToEntries(obj) {
|
|
1030
|
-
return Object.entries(obj);
|
|
1031
|
-
}
|
|
1032
|
-
function deepClone(obj) {
|
|
1033
|
-
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
1034
|
-
}
|
|
1035
|
-
function toUnixPath(path) {
|
|
1036
|
-
return path.replace(/\\/g, "/");
|
|
1037
|
-
}
|
|
1038
|
-
function capitalize(text) {
|
|
1039
|
-
return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
|
|
1040
|
-
1
|
|
1041
|
-
)}`;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
// packages/utils/src/lib/text-formats/table.ts
|
|
1045
|
-
function rowToStringArray({ rows, columns = [] }) {
|
|
1046
|
-
if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
|
|
1047
|
-
throw new TypeError(
|
|
1048
|
-
"Column can`t be object when rows are primitive values"
|
|
1049
|
-
);
|
|
1050
|
-
}
|
|
1051
|
-
return rows.map((row) => {
|
|
1052
|
-
if (Array.isArray(row)) {
|
|
1053
|
-
return row.map(String);
|
|
1054
|
-
}
|
|
1055
|
-
const objectRow = row;
|
|
1056
|
-
if (columns.length === 0 || typeof columns.at(0) === "string") {
|
|
1057
|
-
return Object.values(objectRow).map(
|
|
1058
|
-
(value) => value == null ? "" : String(value)
|
|
1059
|
-
);
|
|
1060
|
-
}
|
|
1061
|
-
return columns.map(
|
|
1062
|
-
({ key }) => objectRow[key] == null ? "" : String(objectRow[key])
|
|
1063
|
-
);
|
|
1064
|
-
});
|
|
1065
|
-
}
|
|
1066
|
-
function columnsToStringArray({
|
|
1067
|
-
rows,
|
|
1068
|
-
columns = []
|
|
1069
|
-
}) {
|
|
1070
|
-
const firstRow = rows.at(0);
|
|
1071
|
-
const primitiveRows = Array.isArray(firstRow);
|
|
1072
|
-
if (typeof columns.at(0) === "string" && !primitiveRows) {
|
|
1073
|
-
throw new Error("invalid union type. Caught by model parsing.");
|
|
1074
|
-
}
|
|
1075
|
-
if (columns.length === 0) {
|
|
1076
|
-
if (Array.isArray(firstRow)) {
|
|
1077
|
-
return firstRow.map((_, idx) => String(idx));
|
|
1078
|
-
}
|
|
1079
|
-
return Object.keys(firstRow);
|
|
1080
|
-
}
|
|
1081
|
-
if (typeof columns.at(0) === "string") {
|
|
1082
|
-
return columns.map(String);
|
|
1083
|
-
}
|
|
1084
|
-
const cols = columns;
|
|
1085
|
-
return cols.map(({ label, key }) => label ?? capitalize(key));
|
|
1086
|
-
}
|
|
1087
|
-
function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
|
|
1088
|
-
const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
|
|
1089
|
-
if (typeof column === "string") {
|
|
1090
|
-
return column;
|
|
1091
|
-
} else if (typeof column === "object") {
|
|
1092
|
-
return column.align ?? "center";
|
|
1093
|
-
} else {
|
|
1094
|
-
return "center";
|
|
1095
|
-
}
|
|
1096
|
-
}
|
|
1097
|
-
function getColumnAlignmentForIndex(targetIdx, columns = []) {
|
|
1098
|
-
const column = columns.at(targetIdx);
|
|
1099
|
-
if (column == null) {
|
|
1100
|
-
return "center";
|
|
1101
|
-
} else if (typeof column === "string") {
|
|
1102
|
-
return column;
|
|
1103
|
-
} else if (typeof column === "object") {
|
|
1104
|
-
return column.align ?? "center";
|
|
1105
|
-
} else {
|
|
1106
|
-
return "center";
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
function getColumnAlignments(tableData) {
|
|
1110
|
-
const { rows, columns = [] } = tableData;
|
|
1111
|
-
if (rows.at(0) == null) {
|
|
1112
|
-
throw new Error("first row can`t be undefined.");
|
|
1113
|
-
}
|
|
1114
|
-
if (Array.isArray(rows.at(0))) {
|
|
1115
|
-
const firstPrimitiveRow = rows.at(0);
|
|
1116
|
-
return Array.from({ length: firstPrimitiveRow.length }).map(
|
|
1117
|
-
(_, idx) => getColumnAlignmentForIndex(idx, columns)
|
|
1118
|
-
);
|
|
1119
|
-
}
|
|
1120
|
-
const biggestRow = [...rows].sort((a, b) => Object.keys(a).length - Object.keys(b).length).at(-1);
|
|
1121
|
-
if (columns.length > 0) {
|
|
1122
|
-
return columns.map(
|
|
1123
|
-
(column, idx) => typeof column === "string" ? column : getColumnAlignmentForKeyAndIndex(
|
|
1124
|
-
column.key,
|
|
1125
|
-
idx,
|
|
1126
|
-
columns
|
|
1127
|
-
)
|
|
1128
|
-
);
|
|
1129
|
-
}
|
|
1130
|
-
return Object.keys(biggestRow ?? {}).map((_) => "center");
|
|
1131
|
-
}
|
|
1132
|
-
|
|
1133
|
-
// packages/utils/src/lib/text-formats/html/table.ts
|
|
1134
|
-
function wrap(elem, content) {
|
|
1135
|
-
return `<${elem}>${content}</${elem}>${NEW_LINE}`;
|
|
1136
|
-
}
|
|
1137
|
-
function wrapRow(content) {
|
|
1138
|
-
const elem = "tr";
|
|
1139
|
-
return `<${elem}>${NEW_LINE}${content}</${elem}>${NEW_LINE}`;
|
|
1140
|
-
}
|
|
1141
|
-
function table(tableData) {
|
|
1142
|
-
if (tableData.rows.length === 0) {
|
|
1143
|
-
throw new Error("Data can't be empty");
|
|
1144
|
-
}
|
|
1145
|
-
const tableHeaderCols = columnsToStringArray(tableData).map((s) => wrap("th", s)).join("");
|
|
1146
|
-
const tableHeaderRow = wrapRow(tableHeaderCols);
|
|
1147
|
-
const tableBody = rowToStringArray(tableData).map((arr) => {
|
|
1148
|
-
const columns = arr.map((s) => wrap("td", s)).join("");
|
|
1149
|
-
return wrapRow(columns);
|
|
1150
|
-
}).join("");
|
|
1151
|
-
return wrap("table", `${NEW_LINE}${tableHeaderRow}${tableBody}`);
|
|
1152
|
-
}
|
|
1153
|
-
|
|
1154
|
-
// packages/utils/src/lib/text-formats/md/font-style.ts
|
|
1155
|
-
var boldWrap = "**";
|
|
1156
|
-
function bold2(text) {
|
|
1157
|
-
return `${boldWrap}${text}${boldWrap}`;
|
|
1158
|
-
}
|
|
1159
|
-
var italicWrap = "_";
|
|
1160
|
-
function italic2(text) {
|
|
1161
|
-
return `${italicWrap}${text}${italicWrap}`;
|
|
1162
|
-
}
|
|
1163
|
-
var strikeThroughWrap = "~";
|
|
1164
|
-
function strikeThrough(text) {
|
|
1165
|
-
return `${strikeThroughWrap}${text}${strikeThroughWrap}`;
|
|
1166
|
-
}
|
|
1167
|
-
var codeWrap = "`";
|
|
1168
|
-
function code2(text) {
|
|
1169
|
-
return `${codeWrap}${text}${codeWrap}`;
|
|
1170
|
-
}
|
|
1171
|
-
|
|
1172
|
-
// packages/utils/src/lib/text-formats/md/headline.ts
|
|
1173
|
-
function headline(text, hierarchy = 1) {
|
|
1174
|
-
return `${"#".repeat(hierarchy)} ${text}${NEW_LINE}`;
|
|
1175
|
-
}
|
|
1176
|
-
function h(text, hierarchy = 1) {
|
|
1177
|
-
return headline(text, hierarchy);
|
|
1178
|
-
}
|
|
1179
|
-
function h1(text) {
|
|
1180
|
-
return headline(text, 1);
|
|
1181
|
-
}
|
|
1182
|
-
function h2(text) {
|
|
1183
|
-
return headline(text, 2);
|
|
1184
|
-
}
|
|
1185
|
-
function h3(text) {
|
|
1186
|
-
return headline(text, 3);
|
|
1187
|
-
}
|
|
1188
|
-
function h4(text) {
|
|
1189
|
-
return headline(text, 4);
|
|
1190
|
-
}
|
|
1191
|
-
function h5(text) {
|
|
1192
|
-
return headline(text, 5);
|
|
1193
|
-
}
|
|
1194
|
-
function h6(text) {
|
|
1195
|
-
return headline(text, 6);
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
// packages/utils/src/lib/text-formats/md/image.ts
|
|
1199
|
-
function image(src, alt) {
|
|
1200
|
-
return ``;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
// packages/utils/src/lib/text-formats/md/link.ts
|
|
1204
|
-
function link3(href, text) {
|
|
1205
|
-
return `[${text || href}](${href})`;
|
|
1206
|
-
}
|
|
1207
|
-
|
|
1208
|
-
// packages/utils/src/lib/text-formats/md/list.ts
|
|
1209
|
-
function li(text, order = "unordered") {
|
|
1210
|
-
const style = order === "unordered" ? "-" : "- [ ]";
|
|
1211
|
-
return `${style} ${text}`;
|
|
1212
|
-
}
|
|
1213
|
-
function indentation(text, level = 1) {
|
|
1214
|
-
return `${TAB.repeat(level)}${text}`;
|
|
1215
|
-
}
|
|
1216
|
-
|
|
1217
|
-
// packages/utils/src/lib/text-formats/md/paragraphs.ts
|
|
1218
|
-
function paragraphs(...sections) {
|
|
1219
|
-
return sections.filter(Boolean).join(`${NEW_LINE}${NEW_LINE}`);
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
// packages/utils/src/lib/text-formats/md/section.ts
|
|
1223
|
-
function section(...contents) {
|
|
1224
|
-
return `${lines(...contents)}${NEW_LINE}`;
|
|
1225
|
-
}
|
|
1226
|
-
function lines(...contents) {
|
|
1227
|
-
const filteredContent = contents.filter(
|
|
1228
|
-
(value) => value != null && value !== "" && value !== false
|
|
1229
|
-
);
|
|
1230
|
-
return `${filteredContent.join(NEW_LINE)}`;
|
|
1231
|
-
}
|
|
1232
|
-
|
|
1233
|
-
// packages/utils/src/lib/text-formats/md/table.ts
|
|
1234
|
-
var alignString = /* @__PURE__ */ new Map([
|
|
1235
|
-
["left", ":--"],
|
|
1236
|
-
["center", ":--:"],
|
|
1237
|
-
["right", "--:"]
|
|
1238
|
-
]);
|
|
1239
|
-
function tableRow(rows) {
|
|
1240
|
-
return `|${rows.join("|")}|`;
|
|
1241
|
-
}
|
|
1242
|
-
function table2(data) {
|
|
1243
|
-
if (data.rows.length === 0) {
|
|
1244
|
-
throw new Error("Data can't be empty");
|
|
1245
|
-
}
|
|
1246
|
-
const alignmentRow = getColumnAlignments(data).map(
|
|
1247
|
-
(s) => alignString.get(s) ?? String(alignString.get("center"))
|
|
1248
|
-
);
|
|
1249
|
-
return section(
|
|
1250
|
-
`${lines(
|
|
1251
|
-
tableRow(columnsToStringArray(data)),
|
|
1252
|
-
tableRow(alignmentRow),
|
|
1253
|
-
...rowToStringArray(data).map(tableRow)
|
|
1254
|
-
)}`
|
|
1255
|
-
);
|
|
1256
|
-
}
|
|
1257
|
-
|
|
1258
|
-
// packages/utils/src/lib/text-formats/index.ts
|
|
1259
|
-
var md = {
|
|
1260
|
-
bold: bold2,
|
|
1261
|
-
italic: italic2,
|
|
1262
|
-
strikeThrough,
|
|
1263
|
-
code: code2,
|
|
1264
|
-
link: link3,
|
|
1265
|
-
image,
|
|
1266
|
-
headline,
|
|
1267
|
-
h,
|
|
1268
|
-
h1,
|
|
1269
|
-
h2,
|
|
1270
|
-
h3,
|
|
1271
|
-
h4,
|
|
1272
|
-
h5,
|
|
1273
|
-
h6,
|
|
1274
|
-
indentation,
|
|
1275
|
-
lines,
|
|
1276
|
-
li,
|
|
1277
|
-
section,
|
|
1278
|
-
paragraphs,
|
|
1279
|
-
table: table2
|
|
1280
|
-
};
|
|
1281
|
-
var html = {
|
|
1282
|
-
bold,
|
|
1283
|
-
italic,
|
|
1284
|
-
code,
|
|
1285
|
-
link: link2,
|
|
1286
|
-
details,
|
|
1287
|
-
table
|
|
1288
|
-
};
|
|
1289
|
-
|
|
1290
|
-
// packages/utils/src/lib/reports/utils.ts
|
|
1291
|
-
var { image: image2, bold: boldMd } = md;
|
|
1292
|
-
function formatReportScore(score) {
|
|
1293
|
-
const scaledScore = score * 100;
|
|
1294
|
-
const roundedScore = Math.round(scaledScore);
|
|
1295
|
-
return roundedScore === 100 && score !== 1 ? Math.floor(scaledScore).toString() : roundedScore.toString();
|
|
1296
|
-
}
|
|
1297
|
-
function formatScoreWithColor(score, options2) {
|
|
1298
|
-
const styledNumber = options2?.skipBold ? formatReportScore(score) : boldMd(formatReportScore(score));
|
|
1299
|
-
return `${scoreMarker(score)} ${styledNumber}`;
|
|
1300
|
-
}
|
|
1301
|
-
var MARKERS = {
|
|
1302
|
-
circle: {
|
|
1303
|
-
red: "\u{1F534}",
|
|
1304
|
-
yellow: "\u{1F7E1}",
|
|
1305
|
-
green: "\u{1F7E2}"
|
|
1306
|
-
},
|
|
1307
|
-
square: {
|
|
1308
|
-
red: "\u{1F7E5}",
|
|
1309
|
-
yellow: "\u{1F7E8}",
|
|
1310
|
-
green: "\u{1F7E9}"
|
|
1311
|
-
}
|
|
1312
|
-
};
|
|
1313
|
-
function scoreMarker(score, markerType = "circle") {
|
|
1314
|
-
if (score >= SCORE_COLOR_RANGE.GREEN_MIN) {
|
|
1315
|
-
return MARKERS[markerType].green;
|
|
1316
|
-
}
|
|
1317
|
-
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
1318
|
-
return MARKERS[markerType].yellow;
|
|
1319
|
-
}
|
|
1320
|
-
return MARKERS[markerType].red;
|
|
1321
|
-
}
|
|
1322
|
-
function getDiffMarker(diff) {
|
|
1323
|
-
if (diff > 0) {
|
|
1324
|
-
return "\u2191";
|
|
1325
|
-
}
|
|
1326
|
-
if (diff < 0) {
|
|
1327
|
-
return "\u2193";
|
|
1328
|
-
}
|
|
1329
|
-
return "";
|
|
1330
|
-
}
|
|
1331
|
-
function colorByScoreDiff(text, diff) {
|
|
1332
|
-
const color = diff > 0 ? "green" : diff < 0 ? "red" : "gray";
|
|
1333
|
-
return shieldsBadge(text, color);
|
|
1334
|
-
}
|
|
1335
|
-
function shieldsBadge(text, color) {
|
|
1336
|
-
return image2(
|
|
1337
|
-
`https://img.shields.io/badge/${encodeURIComponent(text)}-${color}`,
|
|
1338
|
-
text
|
|
1339
|
-
);
|
|
1340
|
-
}
|
|
1341
|
-
function formatDiffNumber(diff) {
|
|
1342
|
-
const number = Math.abs(diff) === Number.POSITIVE_INFINITY ? "\u221E" : `${Math.abs(diff)}`;
|
|
1343
|
-
const sign = diff < 0 ? "\u2212" : "+";
|
|
1344
|
-
return `${sign}${number}`;
|
|
1345
|
-
}
|
|
1346
|
-
function severityMarker(severity) {
|
|
1347
|
-
if (severity === "error") {
|
|
1348
|
-
return "\u{1F6A8}";
|
|
1349
|
-
}
|
|
1350
|
-
if (severity === "warning") {
|
|
1351
|
-
return "\u26A0\uFE0F";
|
|
1352
|
-
}
|
|
1353
|
-
return "\u2139\uFE0F";
|
|
1354
|
-
}
|
|
1355
|
-
function calcDuration(start, stop) {
|
|
1356
|
-
return Math.round((stop ?? performance.now()) - start);
|
|
1357
|
-
}
|
|
1358
|
-
function countCategoryAudits(refs, plugins) {
|
|
1359
|
-
const groupLookup = plugins.reduce(
|
|
1360
|
-
(lookup, plugin) => {
|
|
1361
|
-
if (plugin.groups == null || plugin.groups.length === 0) {
|
|
1362
|
-
return lookup;
|
|
1363
|
-
}
|
|
1364
|
-
return {
|
|
1365
|
-
...lookup,
|
|
1366
|
-
[plugin.slug]: Object.fromEntries(
|
|
1367
|
-
plugin.groups.map((group) => [group.slug, group])
|
|
1368
|
-
)
|
|
1369
|
-
};
|
|
1370
|
-
},
|
|
1371
|
-
{}
|
|
1372
|
-
);
|
|
1373
|
-
return refs.reduce((acc, ref) => {
|
|
1374
|
-
if (ref.type === "group") {
|
|
1375
|
-
const groupRefs = groupLookup[ref.plugin]?.[ref.slug]?.refs;
|
|
1376
|
-
return acc + (groupRefs?.length ?? 0);
|
|
1377
|
-
}
|
|
1378
|
-
return acc + 1;
|
|
1379
|
-
}, 0);
|
|
1380
|
-
}
|
|
1381
|
-
function getSortableAuditByRef({ slug, weight, plugin }, plugins) {
|
|
1382
|
-
const auditPlugin = plugins.find((p) => p.slug === plugin);
|
|
1383
|
-
if (!auditPlugin) {
|
|
1384
|
-
throwIsNotPresentError(`Plugin ${plugin}`, "report");
|
|
1385
|
-
}
|
|
1386
|
-
const audit = auditPlugin.audits.find(
|
|
1387
|
-
({ slug: auditSlug }) => auditSlug === slug
|
|
1388
|
-
);
|
|
1389
|
-
if (!audit) {
|
|
1390
|
-
throwIsNotPresentError(`Audit ${slug}`, auditPlugin.slug);
|
|
1391
|
-
}
|
|
1392
|
-
return {
|
|
1393
|
-
...audit,
|
|
1394
|
-
weight,
|
|
1395
|
-
plugin
|
|
1396
|
-
};
|
|
1397
|
-
}
|
|
1398
|
-
function getSortableGroupByRef({ plugin, slug, weight }, plugins) {
|
|
1399
|
-
const groupPlugin = plugins.find((p) => p.slug === plugin);
|
|
1400
|
-
if (!groupPlugin) {
|
|
1401
|
-
throwIsNotPresentError(`Plugin ${plugin}`, "report");
|
|
1402
|
-
}
|
|
1403
|
-
const group = groupPlugin.groups?.find(
|
|
1404
|
-
({ slug: groupSlug }) => groupSlug === slug
|
|
1405
|
-
);
|
|
1406
|
-
if (!group) {
|
|
1407
|
-
throwIsNotPresentError(`Group ${slug}`, groupPlugin.slug);
|
|
1408
|
-
}
|
|
1409
|
-
const sortedAudits = getSortedGroupAudits(group, groupPlugin.slug, plugins);
|
|
1410
|
-
const sortedAuditRefs = [...group.refs].sort((a, b) => {
|
|
1411
|
-
const aIndex = sortedAudits.findIndex((ref) => ref.slug === a.slug);
|
|
1412
|
-
const bIndex = sortedAudits.findIndex((ref) => ref.slug === b.slug);
|
|
1413
|
-
return aIndex - bIndex;
|
|
1414
|
-
});
|
|
1415
|
-
return {
|
|
1416
|
-
...group,
|
|
1417
|
-
refs: sortedAuditRefs,
|
|
1418
|
-
plugin,
|
|
1419
|
-
weight
|
|
1420
|
-
};
|
|
1421
|
-
}
|
|
1422
|
-
function getSortedGroupAudits(group, plugin, plugins) {
|
|
1423
|
-
return group.refs.map(
|
|
1424
|
-
(ref) => getSortableAuditByRef(
|
|
1425
|
-
{
|
|
1426
|
-
plugin,
|
|
1427
|
-
slug: ref.slug,
|
|
1428
|
-
weight: ref.weight,
|
|
1429
|
-
type: "audit"
|
|
1430
|
-
},
|
|
1431
|
-
plugins
|
|
1432
|
-
)
|
|
1433
|
-
).sort(compareCategoryAuditsAndGroups);
|
|
1434
|
-
}
|
|
1435
|
-
function compareCategoryAuditsAndGroups(a, b) {
|
|
1436
|
-
if (a.weight !== b.weight) {
|
|
1437
|
-
return b.weight - a.weight;
|
|
1438
|
-
}
|
|
1439
|
-
if (a.score !== b.score) {
|
|
1440
|
-
return a.score - b.score;
|
|
1441
|
-
}
|
|
1442
|
-
if ("value" in a && "value" in b && a.value !== b.value) {
|
|
1443
|
-
return b.value - a.value;
|
|
1444
|
-
}
|
|
1445
|
-
return a.title.localeCompare(b.title);
|
|
1446
|
-
}
|
|
1447
|
-
function compareAudits(a, b) {
|
|
1448
|
-
if (a.score !== b.score) {
|
|
1449
|
-
return a.score - b.score;
|
|
1450
|
-
}
|
|
1451
|
-
if (a.value !== b.value) {
|
|
1452
|
-
return b.value - a.value;
|
|
1453
|
-
}
|
|
1454
|
-
return a.title.localeCompare(b.title);
|
|
1455
|
-
}
|
|
1456
|
-
function compareIssueSeverity(severity1, severity2) {
|
|
1457
|
-
const levels = {
|
|
1458
|
-
info: 0,
|
|
1459
|
-
warning: 1,
|
|
1460
|
-
error: 2
|
|
1461
|
-
};
|
|
1462
|
-
return levels[severity1] - levels[severity2];
|
|
1463
|
-
}
|
|
1464
|
-
async function loadReport(options2) {
|
|
1465
|
-
const { outputDir, filename, format } = options2;
|
|
1466
|
-
await ensureDirectoryExists(outputDir);
|
|
1467
|
-
const filePath = join(outputDir, `${filename}.${format}`);
|
|
1468
|
-
if (format === "json") {
|
|
1469
|
-
const content = await readJsonFile(filePath);
|
|
1470
|
-
return reportSchema.parse(content);
|
|
1471
|
-
}
|
|
1472
|
-
const text = await readTextFile(filePath);
|
|
1473
|
-
return text;
|
|
1474
|
-
}
|
|
1475
|
-
function throwIsNotPresentError(itemName, presentPlace) {
|
|
1476
|
-
throw new Error(`${itemName} is not present in ${presentPlace}`);
|
|
1477
|
-
}
|
|
1478
|
-
function getPluginNameFromSlug(slug, plugins) {
|
|
1479
|
-
return plugins.find(({ slug: pluginSlug }) => pluginSlug === slug)?.title || slug;
|
|
1480
|
-
}
|
|
1481
|
-
function compareIssues(a, b) {
|
|
1482
|
-
if (a.severity !== b.severity) {
|
|
1483
|
-
return -compareIssueSeverity(a.severity, b.severity);
|
|
1484
|
-
}
|
|
1485
|
-
if (!a.source && b.source) {
|
|
1486
|
-
return -1;
|
|
1487
|
-
}
|
|
1488
|
-
if (a.source && !b.source) {
|
|
1489
|
-
return 1;
|
|
1490
|
-
}
|
|
1491
|
-
if (a.source?.file !== b.source?.file) {
|
|
1492
|
-
return a.source?.file.localeCompare(b.source?.file || "") ?? 0;
|
|
1493
|
-
}
|
|
1494
|
-
if (!a.source?.position && b.source?.position) {
|
|
1495
|
-
return -1;
|
|
1496
|
-
}
|
|
1497
|
-
if (a.source?.position && !b.source?.position) {
|
|
1498
|
-
return 1;
|
|
1499
|
-
}
|
|
1500
|
-
if (a.source?.position?.startLine !== b.source?.position?.startLine) {
|
|
1501
|
-
return (a.source?.position?.startLine ?? 0) - (b.source?.position?.startLine ?? 0);
|
|
1502
|
-
}
|
|
1503
|
-
return 0;
|
|
1504
|
-
}
|
|
1505
|
-
|
|
1506
|
-
// packages/utils/src/lib/execute-process.ts
|
|
1507
|
-
var ProcessError = class extends Error {
|
|
1508
|
-
code;
|
|
1509
|
-
stderr;
|
|
1510
|
-
stdout;
|
|
1511
|
-
constructor(result) {
|
|
1512
|
-
super(result.stderr);
|
|
1513
|
-
this.code = result.code;
|
|
1514
|
-
this.stderr = result.stderr;
|
|
1515
|
-
this.stdout = result.stdout;
|
|
1188
|
+
const { mod } = await bundleRequire(options2);
|
|
1189
|
+
if (typeof mod === "object" && "default" in mod) {
|
|
1190
|
+
return mod.default;
|
|
1516
1191
|
}
|
|
1517
|
-
|
|
1518
|
-
function executeProcess(cfg) {
|
|
1519
|
-
const { observer, cwd, command: command2, args, ignoreExitCode = false } = cfg;
|
|
1520
|
-
const { onStdout, onError, onComplete } = observer ?? {};
|
|
1521
|
-
const date = (/* @__PURE__ */ new Date()).toISOString();
|
|
1522
|
-
const start = performance.now();
|
|
1523
|
-
return new Promise((resolve, reject) => {
|
|
1524
|
-
const process2 = spawn(command2, args, { cwd, shell: true });
|
|
1525
|
-
let stdout = "";
|
|
1526
|
-
let stderr = "";
|
|
1527
|
-
process2.stdout.on("data", (data) => {
|
|
1528
|
-
stdout += String(data);
|
|
1529
|
-
onStdout?.(String(data));
|
|
1530
|
-
});
|
|
1531
|
-
process2.stderr.on("data", (data) => {
|
|
1532
|
-
stderr += String(data);
|
|
1533
|
-
});
|
|
1534
|
-
process2.on("error", (err) => {
|
|
1535
|
-
stderr += err.toString();
|
|
1536
|
-
});
|
|
1537
|
-
process2.on("close", (code3) => {
|
|
1538
|
-
const timings = { date, duration: calcDuration(start) };
|
|
1539
|
-
if (code3 === 0 || ignoreExitCode) {
|
|
1540
|
-
onComplete?.();
|
|
1541
|
-
resolve({ code: code3, stdout, stderr, ...timings });
|
|
1542
|
-
} else {
|
|
1543
|
-
const errorMsg = new ProcessError({ code: code3, stdout, stderr, ...timings });
|
|
1544
|
-
onError?.(errorMsg);
|
|
1545
|
-
reject(errorMsg);
|
|
1546
|
-
}
|
|
1547
|
-
});
|
|
1548
|
-
});
|
|
1192
|
+
return mod;
|
|
1549
1193
|
}
|
|
1550
1194
|
|
|
1551
1195
|
// packages/utils/src/lib/filter.ts
|
|
@@ -1557,13 +1201,34 @@ function filterItemRefsBy(items, refFilterFn) {
|
|
|
1557
1201
|
}
|
|
1558
1202
|
|
|
1559
1203
|
// packages/utils/src/lib/git/git.ts
|
|
1560
|
-
import { isAbsolute, join
|
|
1204
|
+
import { isAbsolute, join, relative } from "node:path";
|
|
1561
1205
|
import { simpleGit } from "simple-git";
|
|
1206
|
+
|
|
1207
|
+
// packages/utils/src/lib/transform.ts
|
|
1208
|
+
function toArray(val) {
|
|
1209
|
+
return Array.isArray(val) ? val : [val];
|
|
1210
|
+
}
|
|
1211
|
+
function objectToEntries(obj) {
|
|
1212
|
+
return Object.entries(obj);
|
|
1213
|
+
}
|
|
1214
|
+
function deepClone(obj) {
|
|
1215
|
+
return obj == null || typeof obj !== "object" ? obj : structuredClone(obj);
|
|
1216
|
+
}
|
|
1217
|
+
function toUnixPath(path) {
|
|
1218
|
+
return path.replace(/\\/g, "/");
|
|
1219
|
+
}
|
|
1220
|
+
function capitalize(text) {
|
|
1221
|
+
return `${text.charAt(0).toLocaleUpperCase()}${text.slice(
|
|
1222
|
+
1
|
|
1223
|
+
)}`;
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
// packages/utils/src/lib/git/git.ts
|
|
1562
1227
|
function getGitRoot(git = simpleGit()) {
|
|
1563
1228
|
return git.revparse("--show-toplevel");
|
|
1564
1229
|
}
|
|
1565
1230
|
function formatGitPath(path, gitRoot) {
|
|
1566
|
-
const absolutePath = isAbsolute(path) ? path :
|
|
1231
|
+
const absolutePath = isAbsolute(path) ? path : join(process.cwd(), path);
|
|
1567
1232
|
const relativePath = relative(gitRoot, absolutePath);
|
|
1568
1233
|
return toUnixPath(relativePath);
|
|
1569
1234
|
}
|
|
@@ -1735,17 +1400,17 @@ function groupByStatus(results) {
|
|
|
1735
1400
|
}
|
|
1736
1401
|
|
|
1737
1402
|
// packages/utils/src/lib/progress.ts
|
|
1738
|
-
import
|
|
1403
|
+
import { black, bold as bold2, gray as gray2, green } from "ansis";
|
|
1739
1404
|
import { MultiProgressBars } from "multi-progress-bars";
|
|
1740
1405
|
var barStyles = {
|
|
1741
|
-
active: (s) =>
|
|
1742
|
-
done: (s) =>
|
|
1743
|
-
idle: (s) =>
|
|
1406
|
+
active: (s) => green(s),
|
|
1407
|
+
done: (s) => gray2(s),
|
|
1408
|
+
idle: (s) => gray2(s)
|
|
1744
1409
|
};
|
|
1745
1410
|
var messageStyles = {
|
|
1746
|
-
active: (s) =>
|
|
1747
|
-
done: (s) =>
|
|
1748
|
-
idle: (s) =>
|
|
1411
|
+
active: (s) => black(s),
|
|
1412
|
+
done: (s) => bold2.green(s),
|
|
1413
|
+
idle: (s) => gray2(s)
|
|
1749
1414
|
};
|
|
1750
1415
|
var mpb;
|
|
1751
1416
|
function getSingletonProgressBars(options2) {
|
|
@@ -1788,334 +1453,502 @@ function getProgressBar(taskName) {
|
|
|
1788
1453
|
}
|
|
1789
1454
|
};
|
|
1790
1455
|
}
|
|
1791
|
-
|
|
1792
|
-
// packages/utils/src/lib/reports/flatten-plugins.ts
|
|
1793
|
-
function listGroupsFromAllPlugins(report) {
|
|
1794
|
-
return report.plugins.flatMap(
|
|
1795
|
-
(plugin) => plugin.groups?.map((group) => ({ plugin, group })) ?? []
|
|
1796
|
-
);
|
|
1797
|
-
}
|
|
1798
|
-
function listAuditsFromAllPlugins(report) {
|
|
1799
|
-
return report.plugins.flatMap(
|
|
1800
|
-
(plugin) => plugin.audits.map((audit) => ({ plugin, audit }))
|
|
1801
|
-
);
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
// packages/utils/src/lib/reports/
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1456
|
+
|
|
1457
|
+
// packages/utils/src/lib/reports/flatten-plugins.ts
|
|
1458
|
+
function listGroupsFromAllPlugins(report) {
|
|
1459
|
+
return report.plugins.flatMap(
|
|
1460
|
+
(plugin) => plugin.groups?.map((group) => ({ plugin, group })) ?? []
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
function listAuditsFromAllPlugins(report) {
|
|
1464
|
+
return report.plugins.flatMap(
|
|
1465
|
+
(plugin) => plugin.audits.map((audit) => ({ plugin, audit }))
|
|
1466
|
+
);
|
|
1467
|
+
}
|
|
1468
|
+
|
|
1469
|
+
// packages/utils/src/lib/reports/generate-md-report.ts
|
|
1470
|
+
import { MarkdownDocument as MarkdownDocument3, md as md4 } from "build-md";
|
|
1471
|
+
|
|
1472
|
+
// packages/utils/src/lib/text-formats/constants.ts
|
|
1473
|
+
var HIERARCHY = {
|
|
1474
|
+
level_1: 1,
|
|
1475
|
+
level_2: 2,
|
|
1476
|
+
level_3: 3,
|
|
1477
|
+
level_4: 4,
|
|
1478
|
+
level_5: 5,
|
|
1479
|
+
level_6: 6
|
|
1480
|
+
};
|
|
1481
|
+
|
|
1482
|
+
// packages/utils/src/lib/text-formats/table.ts
|
|
1483
|
+
function rowToStringArray({ rows, columns = [] }) {
|
|
1484
|
+
if (Array.isArray(rows.at(0)) && typeof columns.at(0) === "object") {
|
|
1485
|
+
throw new TypeError(
|
|
1486
|
+
"Column can`t be object when rows are primitive values"
|
|
1487
|
+
);
|
|
1488
|
+
}
|
|
1489
|
+
return rows.map((row) => {
|
|
1490
|
+
if (Array.isArray(row)) {
|
|
1491
|
+
return row.map(String);
|
|
1492
|
+
}
|
|
1493
|
+
const objectRow = row;
|
|
1494
|
+
if (columns.length === 0 || typeof columns.at(0) === "string") {
|
|
1495
|
+
return Object.values(objectRow).map(
|
|
1496
|
+
(value) => value == null ? "" : String(value)
|
|
1497
|
+
);
|
|
1498
|
+
}
|
|
1499
|
+
return columns.map(
|
|
1500
|
+
({ key }) => objectRow[key] == null ? "" : String(objectRow[key])
|
|
1501
|
+
);
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
function columnsToStringArray({
|
|
1505
|
+
rows,
|
|
1506
|
+
columns = []
|
|
1507
|
+
}) {
|
|
1508
|
+
const firstRow = rows.at(0);
|
|
1509
|
+
const primitiveRows = Array.isArray(firstRow);
|
|
1510
|
+
if (typeof columns.at(0) === "string" && !primitiveRows) {
|
|
1511
|
+
throw new Error("invalid union type. Caught by model parsing.");
|
|
1512
|
+
}
|
|
1513
|
+
if (columns.length === 0) {
|
|
1514
|
+
if (Array.isArray(firstRow)) {
|
|
1515
|
+
return firstRow.map((_, idx) => String(idx));
|
|
1516
|
+
}
|
|
1517
|
+
return Object.keys(firstRow);
|
|
1518
|
+
}
|
|
1519
|
+
if (typeof columns.at(0) === "string") {
|
|
1520
|
+
return columns.map(String);
|
|
1521
|
+
}
|
|
1522
|
+
const cols = columns;
|
|
1523
|
+
return cols.map(({ label, key }) => label ?? capitalize(key));
|
|
1524
|
+
}
|
|
1525
|
+
function getColumnAlignmentForKeyAndIndex(targetKey, targetIdx, columns = []) {
|
|
1526
|
+
const column = columns.at(targetIdx) ?? columns.find((col) => col.key === targetKey);
|
|
1527
|
+
if (typeof column === "string") {
|
|
1528
|
+
return column;
|
|
1529
|
+
} else if (typeof column === "object") {
|
|
1530
|
+
return column.align ?? "center";
|
|
1531
|
+
} else {
|
|
1532
|
+
return "center";
|
|
1533
|
+
}
|
|
1534
|
+
}
|
|
1535
|
+
function getColumnAlignmentForIndex(targetIdx, columns = []) {
|
|
1536
|
+
const column = columns.at(targetIdx);
|
|
1537
|
+
if (column == null) {
|
|
1538
|
+
return "center";
|
|
1539
|
+
} else if (typeof column === "string") {
|
|
1540
|
+
return column;
|
|
1541
|
+
} else if (typeof column === "object") {
|
|
1542
|
+
return column.align ?? "center";
|
|
1543
|
+
} else {
|
|
1544
|
+
return "center";
|
|
1545
|
+
}
|
|
1546
|
+
}
|
|
1547
|
+
function getColumnAlignments(tableData) {
|
|
1548
|
+
const { rows, columns = [] } = tableData;
|
|
1549
|
+
if (rows.at(0) == null) {
|
|
1550
|
+
throw new Error("first row can`t be undefined.");
|
|
1551
|
+
}
|
|
1552
|
+
if (Array.isArray(rows.at(0))) {
|
|
1553
|
+
const firstPrimitiveRow = rows.at(0);
|
|
1554
|
+
return Array.from({ length: firstPrimitiveRow.length }).map(
|
|
1555
|
+
(_, idx) => getColumnAlignmentForIndex(idx, columns)
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
const biggestRow = [...rows].sort((a, b) => Object.keys(a).length - Object.keys(b).length).at(-1);
|
|
1559
|
+
if (columns.length > 0) {
|
|
1560
|
+
return columns.map(
|
|
1561
|
+
(column, idx) => typeof column === "string" ? column : getColumnAlignmentForKeyAndIndex(
|
|
1562
|
+
column.key,
|
|
1563
|
+
idx,
|
|
1564
|
+
columns
|
|
1565
|
+
)
|
|
1566
|
+
);
|
|
1567
|
+
}
|
|
1568
|
+
return Object.keys(biggestRow ?? {}).map((_) => "center");
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
// packages/utils/src/lib/reports/formatting.ts
|
|
1572
|
+
import { MarkdownDocument, md as md2 } from "build-md";
|
|
1573
|
+
function tableSection(tableData, options2) {
|
|
1574
|
+
if (tableData.rows.length === 0) {
|
|
1575
|
+
return null;
|
|
1576
|
+
}
|
|
1577
|
+
const { level = HIERARCHY.level_4 } = options2 ?? {};
|
|
1578
|
+
const columns = columnsToStringArray(tableData);
|
|
1579
|
+
const alignments = getColumnAlignments(tableData);
|
|
1580
|
+
const rows = rowToStringArray(tableData);
|
|
1581
|
+
return new MarkdownDocument().heading(level, tableData.title).table(
|
|
1582
|
+
columns.map((heading, i) => {
|
|
1583
|
+
const alignment = alignments[i];
|
|
1584
|
+
if (alignment) {
|
|
1585
|
+
return { heading, alignment };
|
|
1586
|
+
}
|
|
1587
|
+
return heading;
|
|
1588
|
+
}),
|
|
1589
|
+
rows
|
|
1590
|
+
);
|
|
1591
|
+
}
|
|
1592
|
+
function metaDescription(audit) {
|
|
1593
|
+
const docsUrl = audit.docsUrl;
|
|
1594
|
+
const description = audit.description?.trim();
|
|
1595
|
+
if (docsUrl) {
|
|
1596
|
+
const docsLink = md2.link(docsUrl, "\u{1F4D6} Docs");
|
|
1597
|
+
if (!description) {
|
|
1598
|
+
return docsLink;
|
|
1599
|
+
}
|
|
1600
|
+
const parsedDescription = description.endsWith("```") ? `${description}
|
|
1601
|
+
|
|
1602
|
+
` : `${description} `;
|
|
1603
|
+
return md2`${parsedDescription}${docsLink}`;
|
|
1604
|
+
}
|
|
1605
|
+
if (description && description.trim().length > 0) {
|
|
1606
|
+
return description;
|
|
1607
|
+
}
|
|
1608
|
+
return "";
|
|
1609
|
+
}
|
|
1610
|
+
|
|
1611
|
+
// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
|
|
1612
|
+
import { MarkdownDocument as MarkdownDocument2, md as md3 } from "build-md";
|
|
1613
|
+
|
|
1614
|
+
// packages/utils/src/lib/reports/sorting.ts
|
|
1615
|
+
function getSortableAuditByRef({ slug, weight, plugin }, plugins) {
|
|
1616
|
+
const auditPlugin = plugins.find((p) => p.slug === plugin);
|
|
1617
|
+
if (!auditPlugin) {
|
|
1618
|
+
throwIsNotPresentError(`Plugin ${plugin}`, "report");
|
|
1619
|
+
}
|
|
1620
|
+
const audit = auditPlugin.audits.find(
|
|
1621
|
+
({ slug: auditSlug }) => auditSlug === slug
|
|
1622
|
+
);
|
|
1623
|
+
if (!audit) {
|
|
1624
|
+
throwIsNotPresentError(`Audit ${slug}`, auditPlugin.slug);
|
|
1625
|
+
}
|
|
1626
|
+
return {
|
|
1627
|
+
...audit,
|
|
1628
|
+
weight,
|
|
1629
|
+
plugin
|
|
1630
|
+
};
|
|
1631
|
+
}
|
|
1632
|
+
function getSortedGroupAudits(group, plugin, plugins) {
|
|
1633
|
+
return group.refs.map(
|
|
1634
|
+
(ref) => getSortableAuditByRef(
|
|
1635
|
+
{
|
|
1636
|
+
plugin,
|
|
1637
|
+
slug: ref.slug,
|
|
1638
|
+
weight: ref.weight,
|
|
1639
|
+
type: "audit"
|
|
1640
|
+
},
|
|
1641
|
+
plugins
|
|
1642
|
+
)
|
|
1643
|
+
).sort(compareCategoryAuditsAndGroups);
|
|
1644
|
+
}
|
|
1645
|
+
function getSortableGroupByRef({ plugin, slug, weight }, plugins) {
|
|
1646
|
+
const groupPlugin = plugins.find((p) => p.slug === plugin);
|
|
1647
|
+
if (!groupPlugin) {
|
|
1648
|
+
throwIsNotPresentError(`Plugin ${plugin}`, "report");
|
|
1649
|
+
}
|
|
1650
|
+
const group = groupPlugin.groups?.find(
|
|
1651
|
+
({ slug: groupSlug }) => groupSlug === slug
|
|
1652
|
+
);
|
|
1653
|
+
if (!group) {
|
|
1654
|
+
throwIsNotPresentError(`Group ${slug}`, groupPlugin.slug);
|
|
1655
|
+
}
|
|
1656
|
+
const sortedAudits = getSortedGroupAudits(group, groupPlugin.slug, plugins);
|
|
1657
|
+
const sortedAuditRefs = [...group.refs].sort((a, b) => {
|
|
1658
|
+
const aIndex = sortedAudits.findIndex((ref) => ref.slug === a.slug);
|
|
1659
|
+
const bIndex = sortedAudits.findIndex((ref) => ref.slug === b.slug);
|
|
1660
|
+
return aIndex - bIndex;
|
|
1661
|
+
});
|
|
1662
|
+
return {
|
|
1663
|
+
...group,
|
|
1664
|
+
refs: sortedAuditRefs,
|
|
1665
|
+
plugin,
|
|
1666
|
+
weight
|
|
1667
|
+
};
|
|
1668
|
+
}
|
|
1669
|
+
function sortReport(report) {
|
|
1670
|
+
const { categories, plugins } = report;
|
|
1671
|
+
const sortedCategories = categories.map((category) => {
|
|
1672
|
+
const { audits, groups: groups2 } = category.refs.reduce(
|
|
1673
|
+
(acc, ref) => ({
|
|
1674
|
+
...acc,
|
|
1675
|
+
...ref.type === "group" ? {
|
|
1676
|
+
groups: [...acc.groups, getSortableGroupByRef(ref, plugins)]
|
|
1677
|
+
} : {
|
|
1678
|
+
audits: [...acc.audits, getSortableAuditByRef(ref, plugins)]
|
|
1679
|
+
}
|
|
1680
|
+
}),
|
|
1681
|
+
{ groups: [], audits: [] }
|
|
1682
|
+
);
|
|
1683
|
+
const sortedAuditsAndGroups = [...audits, ...groups2].sort(
|
|
1684
|
+
compareCategoryAuditsAndGroups
|
|
1685
|
+
);
|
|
1686
|
+
const sortedRefs = [...category.refs].sort((a, b) => {
|
|
1687
|
+
const aIndex = sortedAuditsAndGroups.findIndex(
|
|
1688
|
+
(ref) => ref.slug === a.slug && ref.plugin === a.plugin
|
|
1689
|
+
);
|
|
1690
|
+
const bIndex = sortedAuditsAndGroups.findIndex(
|
|
1691
|
+
(ref) => ref.slug === b.slug && ref.plugin === b.plugin
|
|
1692
|
+
);
|
|
1693
|
+
return aIndex - bIndex;
|
|
1694
|
+
});
|
|
1695
|
+
return { ...category, refs: sortedRefs };
|
|
1696
|
+
});
|
|
1697
|
+
return {
|
|
1698
|
+
...report,
|
|
1699
|
+
categories: sortedCategories,
|
|
1700
|
+
plugins: sortPlugins(plugins)
|
|
1701
|
+
};
|
|
1702
|
+
}
|
|
1703
|
+
function sortPlugins(plugins) {
|
|
1704
|
+
return plugins.map((plugin) => ({
|
|
1705
|
+
...plugin,
|
|
1706
|
+
audits: [...plugin.audits].sort(compareAudits).map(
|
|
1707
|
+
(audit) => audit.details?.issues ? {
|
|
1708
|
+
...audit,
|
|
1709
|
+
details: {
|
|
1710
|
+
...audit.details,
|
|
1711
|
+
issues: [...audit.details.issues].sort(compareIssues)
|
|
1712
|
+
}
|
|
1713
|
+
} : audit
|
|
1714
|
+
)
|
|
1715
|
+
}));
|
|
1833
1716
|
}
|
|
1834
1717
|
|
|
1835
1718
|
// packages/utils/src/lib/reports/generate-md-report-categoy-section.ts
|
|
1836
|
-
var { link: link5, section: section3, h2: h22, lines: lines3, li: li2, bold: boldMd2, h3: h32, indentation: indentation2 } = md;
|
|
1837
1719
|
function categoriesOverviewSection(report) {
|
|
1838
1720
|
const { categories, plugins } = report;
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
}
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1721
|
+
return new MarkdownDocument2().table(
|
|
1722
|
+
[
|
|
1723
|
+
{ heading: "\u{1F3F7} Category", alignment: "left" },
|
|
1724
|
+
{ heading: "\u2B50 Score", alignment: "center" },
|
|
1725
|
+
{ heading: "\u{1F6E1} Audits", alignment: "center" }
|
|
1726
|
+
],
|
|
1727
|
+
categories.map(({ title, refs, score, isBinary }) => [
|
|
1728
|
+
// @TODO refactor `isBinary: boolean` to `targetScore: number` #713
|
|
1729
|
+
// The heading "ID" is inferred from the heading text in Markdown.
|
|
1730
|
+
md3.link(`#${slugify(title)}`, title),
|
|
1731
|
+
md3`${scoreMarker(score)} ${md3.bold(
|
|
1732
|
+
formatReportScore(score)
|
|
1733
|
+
)}${binaryIconSuffix(score, isBinary)}`,
|
|
1734
|
+
countCategoryAudits(refs, plugins).toString()
|
|
1735
|
+
])
|
|
1736
|
+
);
|
|
1854
1737
|
}
|
|
1855
1738
|
function categoriesDetailsSection(report) {
|
|
1856
1739
|
const { categories, plugins } = report;
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
category.score
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
...categoryMDItems
|
|
1884
|
-
);
|
|
1885
|
-
});
|
|
1886
|
-
return lines3(h22(CATEGORIES_TITLE), ...categoryDetails);
|
|
1740
|
+
return new MarkdownDocument2().heading(HIERARCHY.level_2, "\u{1F3F7} Categories").$foreach(
|
|
1741
|
+
categories,
|
|
1742
|
+
(doc, category) => doc.heading(HIERARCHY.level_3, category.title).paragraph(metaDescription(category)).paragraph(
|
|
1743
|
+
md3`${scoreMarker(category.score)} Score: ${md3.bold(
|
|
1744
|
+
formatReportScore(category.score)
|
|
1745
|
+
)}${binaryIconSuffix(category.score, category.isBinary)}`
|
|
1746
|
+
).list(
|
|
1747
|
+
category.refs.map((ref) => {
|
|
1748
|
+
if (ref.type === "group") {
|
|
1749
|
+
const group = getSortableGroupByRef(ref, plugins);
|
|
1750
|
+
const groupAudits = group.refs.map(
|
|
1751
|
+
(groupRef) => getSortableAuditByRef(
|
|
1752
|
+
{ ...groupRef, plugin: group.plugin, type: "audit" },
|
|
1753
|
+
plugins
|
|
1754
|
+
)
|
|
1755
|
+
);
|
|
1756
|
+
const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
|
|
1757
|
+
return categoryGroupItem(group, groupAudits, pluginTitle);
|
|
1758
|
+
} else {
|
|
1759
|
+
const audit = getSortableAuditByRef(ref, plugins);
|
|
1760
|
+
const pluginTitle = getPluginNameFromSlug(ref.plugin, plugins);
|
|
1761
|
+
return categoryRef(audit, pluginTitle);
|
|
1762
|
+
}
|
|
1763
|
+
})
|
|
1764
|
+
)
|
|
1765
|
+
);
|
|
1887
1766
|
}
|
|
1888
1767
|
function categoryRef({ title, score, value, displayValue }, pluginTitle) {
|
|
1889
|
-
const auditTitleAsLink =
|
|
1768
|
+
const auditTitleAsLink = md3.link(
|
|
1890
1769
|
`#${slugify(title)}-${slugify(pluginTitle)}`,
|
|
1891
1770
|
title
|
|
1892
1771
|
);
|
|
1893
1772
|
const marker = scoreMarker(score, "square");
|
|
1894
|
-
return
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
)}`
|
|
1898
|
-
);
|
|
1773
|
+
return md3`${marker} ${auditTitleAsLink} (${md3.italic(
|
|
1774
|
+
pluginTitle
|
|
1775
|
+
)}) - ${md3.bold((displayValue || value).toString())}`;
|
|
1899
1776
|
}
|
|
1900
1777
|
function categoryGroupItem({ score = 0, title }, groupAudits, pluginTitle) {
|
|
1901
|
-
const groupTitle =
|
|
1902
|
-
|
|
1903
|
-
)
|
|
1904
|
-
const
|
|
1905
|
-
(
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
);
|
|
1918
|
-
}
|
|
1778
|
+
const groupTitle = md3`${scoreMarker(score)} ${title} (${md3.italic(
|
|
1779
|
+
pluginTitle
|
|
1780
|
+
)})`;
|
|
1781
|
+
const auditsList = md3.list(
|
|
1782
|
+
groupAudits.map(
|
|
1783
|
+
({ title: auditTitle, score: auditScore, value, displayValue }) => {
|
|
1784
|
+
const auditTitleLink = md3.link(
|
|
1785
|
+
`#${slugify(auditTitle)}-${slugify(pluginTitle)}`,
|
|
1786
|
+
auditTitle
|
|
1787
|
+
);
|
|
1788
|
+
const marker = scoreMarker(auditScore, "square");
|
|
1789
|
+
return md3`${marker} ${auditTitleLink} - ${md3.bold(
|
|
1790
|
+
String(displayValue ?? value)
|
|
1791
|
+
)}`;
|
|
1792
|
+
}
|
|
1793
|
+
)
|
|
1919
1794
|
);
|
|
1920
|
-
return
|
|
1795
|
+
return md3`${groupTitle}${auditsList}`;
|
|
1796
|
+
}
|
|
1797
|
+
function binaryIconSuffix(score, isBinary) {
|
|
1798
|
+
return targetScoreIcon(score, isBinary ? 1 : void 0, { prefix: " " });
|
|
1921
1799
|
}
|
|
1922
1800
|
|
|
1923
1801
|
// packages/utils/src/lib/reports/generate-md-report.ts
|
|
1924
|
-
var { h1: h12, h2: h23, h3: h33, lines: lines4, link: link6, section: section4, code: codeMd } = md;
|
|
1925
|
-
var { bold: boldHtml, details: details2 } = html;
|
|
1926
1802
|
function auditDetailsAuditValue({
|
|
1927
1803
|
score,
|
|
1928
1804
|
value,
|
|
1929
1805
|
displayValue
|
|
1930
1806
|
}) {
|
|
1931
|
-
return `${scoreMarker(score, "square")} ${
|
|
1807
|
+
return md4`${scoreMarker(score, "square")} ${md4.bold(
|
|
1932
1808
|
String(displayValue ?? value)
|
|
1933
1809
|
)} (score: ${formatReportScore(score)})`;
|
|
1934
1810
|
}
|
|
1935
1811
|
function generateMdReport(report) {
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
`${FOOTER_PREFIX}${SPACE}${link6(README_LINK, "Code PushUp")}`
|
|
1944
|
-
);
|
|
1812
|
+
return new MarkdownDocument3().heading(HIERARCHY.level_1, REPORT_HEADLINE_TEXT).$if(
|
|
1813
|
+
report.categories.length > 0,
|
|
1814
|
+
(doc) => doc.$concat(
|
|
1815
|
+
categoriesOverviewSection(report),
|
|
1816
|
+
categoriesDetailsSection(report)
|
|
1817
|
+
)
|
|
1818
|
+
).$concat(auditsSection(report), aboutSection(report)).rule().paragraph(md4`${FOOTER_PREFIX} ${md4.link(README_LINK, "Code PushUp")}`).toString();
|
|
1945
1819
|
}
|
|
1946
1820
|
function auditDetailsIssues(issues = []) {
|
|
1947
1821
|
if (issues.length === 0) {
|
|
1948
|
-
return
|
|
1949
|
-
}
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
return { severity, message, file, line: "" };
|
|
1962
|
-
}
|
|
1963
|
-
const { startLine, endLine } = sourceVal.position;
|
|
1964
|
-
const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
|
|
1965
|
-
return { severity, message, file, line };
|
|
1822
|
+
return null;
|
|
1823
|
+
}
|
|
1824
|
+
return new MarkdownDocument3().heading(HIERARCHY.level_4, "Issues").table(
|
|
1825
|
+
[
|
|
1826
|
+
{ heading: "Severity", alignment: "center" },
|
|
1827
|
+
{ heading: "Message", alignment: "left" },
|
|
1828
|
+
{ heading: "Source file", alignment: "left" },
|
|
1829
|
+
{ heading: "Line(s)", alignment: "center" }
|
|
1830
|
+
],
|
|
1831
|
+
issues.map(({ severity: level, message, source }) => {
|
|
1832
|
+
const severity = md4`${severityMarker(level)} ${md4.italic(level)}`;
|
|
1833
|
+
if (!source) {
|
|
1834
|
+
return [severity, message];
|
|
1966
1835
|
}
|
|
1967
|
-
|
|
1968
|
-
|
|
1969
|
-
|
|
1836
|
+
const file = md4.code(source.file);
|
|
1837
|
+
if (!source.position) {
|
|
1838
|
+
return [severity, message, file];
|
|
1839
|
+
}
|
|
1840
|
+
const { startLine, endLine } = source.position;
|
|
1841
|
+
const line = `${startLine || ""}${endLine && startLine !== endLine ? `-${endLine}` : ""}`;
|
|
1842
|
+
return [severity, message, file, line];
|
|
1843
|
+
})
|
|
1844
|
+
);
|
|
1970
1845
|
}
|
|
1971
1846
|
function auditDetails(audit) {
|
|
1972
|
-
const { table:
|
|
1847
|
+
const { table: table2, issues = [] } = audit.details ?? {};
|
|
1973
1848
|
const detailsValue = auditDetailsAuditValue(audit);
|
|
1974
|
-
if (issues.length === 0 &&
|
|
1975
|
-
return
|
|
1849
|
+
if (issues.length === 0 && !table2?.rows.length) {
|
|
1850
|
+
return new MarkdownDocument3().paragraph(detailsValue);
|
|
1976
1851
|
}
|
|
1977
|
-
const tableSectionContent =
|
|
1978
|
-
const issuesSectionContent = issues.length > 0
|
|
1979
|
-
return
|
|
1852
|
+
const tableSectionContent = table2 && tableSection(table2);
|
|
1853
|
+
const issuesSectionContent = issues.length > 0 && auditDetailsIssues(issues);
|
|
1854
|
+
return new MarkdownDocument3().details(
|
|
1980
1855
|
detailsValue,
|
|
1981
|
-
|
|
1856
|
+
new MarkdownDocument3().$concat(tableSectionContent, issuesSectionContent)
|
|
1982
1857
|
);
|
|
1983
1858
|
}
|
|
1984
1859
|
function auditsSection({
|
|
1985
1860
|
plugins
|
|
1986
1861
|
}) {
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1862
|
+
return new MarkdownDocument3().heading(HIERARCHY.level_2, "\u{1F6E1}\uFE0F Audits").$foreach(
|
|
1863
|
+
plugins.flatMap(
|
|
1864
|
+
(plugin) => plugin.audits.map((audit) => ({ ...audit, plugin }))
|
|
1865
|
+
),
|
|
1866
|
+
(doc, { plugin, ...audit }) => {
|
|
1867
|
+
const auditTitle = `${audit.title} (${plugin.title})`;
|
|
1993
1868
|
const detailsContent = auditDetails(audit);
|
|
1994
1869
|
const descriptionContent = metaDescription(audit);
|
|
1995
|
-
return
|
|
1996
|
-
}
|
|
1870
|
+
return doc.heading(HIERARCHY.level_3, auditTitle).$concat(detailsContent).paragraph(descriptionContent);
|
|
1871
|
+
}
|
|
1997
1872
|
);
|
|
1998
|
-
return section4(h23("\u{1F6E1}\uFE0F Audits"), ...content);
|
|
1999
1873
|
}
|
|
2000
1874
|
function aboutSection(report) {
|
|
2001
1875
|
const { date, plugins } = report;
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
}
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
{
|
|
2019
|
-
key: "plugin",
|
|
2020
|
-
align: "left"
|
|
2021
|
-
},
|
|
2022
|
-
{
|
|
2023
|
-
key: "audits"
|
|
2024
|
-
},
|
|
2025
|
-
{
|
|
2026
|
-
key: "version"
|
|
2027
|
-
},
|
|
2028
|
-
{
|
|
2029
|
-
key: "duration"
|
|
2030
|
-
}
|
|
1876
|
+
return new MarkdownDocument3().heading(HIERARCHY.level_2, "About").paragraph(
|
|
1877
|
+
md4`Report was created by ${md4.link(
|
|
1878
|
+
README_LINK,
|
|
1879
|
+
"Code PushUp"
|
|
1880
|
+
)} on ${formatDate(new Date(date))}.`
|
|
1881
|
+
).table(...pluginMetaTable({ plugins })).table(...reportMetaTable(report));
|
|
1882
|
+
}
|
|
1883
|
+
function pluginMetaTable({
|
|
1884
|
+
plugins
|
|
1885
|
+
}) {
|
|
1886
|
+
return [
|
|
1887
|
+
[
|
|
1888
|
+
{ heading: "Plugin", alignment: "left" },
|
|
1889
|
+
{ heading: "Audits", alignment: "center" },
|
|
1890
|
+
{ heading: "Version", alignment: "center" },
|
|
1891
|
+
{ heading: "Duration", alignment: "right" }
|
|
2031
1892
|
],
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
plugin: pluginTitle,
|
|
2040
|
-
audits: audits.length.toString(),
|
|
2041
|
-
version: codeMd(pluginVersion || ""),
|
|
2042
|
-
duration: formatDuration(pluginDuration)
|
|
2043
|
-
})
|
|
2044
|
-
)
|
|
2045
|
-
};
|
|
1893
|
+
plugins.map(({ title, audits, version: version2 = "", duration }) => [
|
|
1894
|
+
title,
|
|
1895
|
+
audits.length.toString(),
|
|
1896
|
+
version2 && md4.code(version2),
|
|
1897
|
+
formatDuration(duration)
|
|
1898
|
+
])
|
|
1899
|
+
];
|
|
2046
1900
|
}
|
|
2047
|
-
function
|
|
1901
|
+
function reportMetaTable({
|
|
2048
1902
|
commit,
|
|
2049
1903
|
version: version2,
|
|
2050
1904
|
duration,
|
|
2051
1905
|
plugins,
|
|
2052
1906
|
categories
|
|
2053
1907
|
}) {
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
{
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
},
|
|
2061
|
-
{
|
|
2062
|
-
key: "version"
|
|
2063
|
-
},
|
|
2064
|
-
{
|
|
2065
|
-
key: "duration"
|
|
2066
|
-
},
|
|
2067
|
-
{
|
|
2068
|
-
key: "plugins"
|
|
2069
|
-
},
|
|
2070
|
-
{
|
|
2071
|
-
key: "categories"
|
|
2072
|
-
},
|
|
2073
|
-
{
|
|
2074
|
-
key: "audits"
|
|
2075
|
-
}
|
|
1908
|
+
return [
|
|
1909
|
+
[
|
|
1910
|
+
{ heading: "Commit", alignment: "left" },
|
|
1911
|
+
{ heading: "Version", alignment: "center" },
|
|
1912
|
+
{ heading: "Duration", alignment: "right" },
|
|
1913
|
+
{ heading: "Plugins", alignment: "center" },
|
|
1914
|
+
{ heading: "Categories", alignment: "center" },
|
|
1915
|
+
{ heading: "Audits", alignment: "center" }
|
|
2076
1916
|
],
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
commit:
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
plugins
|
|
2083
|
-
categories
|
|
2084
|
-
|
|
2085
|
-
|
|
1917
|
+
[
|
|
1918
|
+
[
|
|
1919
|
+
commit ? `${commit.message} (${commit.hash})` : "N/A",
|
|
1920
|
+
md4.code(version2),
|
|
1921
|
+
formatDuration(duration),
|
|
1922
|
+
plugins.length.toString(),
|
|
1923
|
+
categories.length.toString(),
|
|
1924
|
+
plugins.reduce((acc, { audits }) => acc + audits.length, 0).toString()
|
|
1925
|
+
]
|
|
2086
1926
|
]
|
|
2087
|
-
|
|
1927
|
+
];
|
|
2088
1928
|
}
|
|
2089
1929
|
|
|
2090
1930
|
// packages/utils/src/lib/reports/generate-md-reports-diff.ts
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
link: link7,
|
|
2096
|
-
bold: boldMd3,
|
|
2097
|
-
italic: italicMd,
|
|
2098
|
-
table: table4,
|
|
2099
|
-
section: section5
|
|
2100
|
-
} = md;
|
|
2101
|
-
var { details: details3 } = html;
|
|
1931
|
+
import {
|
|
1932
|
+
MarkdownDocument as MarkdownDocument4,
|
|
1933
|
+
md as md5
|
|
1934
|
+
} from "build-md";
|
|
2102
1935
|
var MAX_ROWS = 100;
|
|
2103
|
-
function generateMdReportsDiff(diff) {
|
|
2104
|
-
return
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
);
|
|
2110
|
-
}
|
|
2111
|
-
function
|
|
1936
|
+
function generateMdReportsDiff(diff, portalUrl) {
|
|
1937
|
+
return new MarkdownDocument4().$concat(
|
|
1938
|
+
createDiffHeaderSection(diff, portalUrl),
|
|
1939
|
+
createDiffCategoriesSection(diff),
|
|
1940
|
+
createDiffGroupsSection(diff),
|
|
1941
|
+
createDiffAuditsSection(diff)
|
|
1942
|
+
).toString();
|
|
1943
|
+
}
|
|
1944
|
+
function createDiffHeaderSection(diff, portalUrl) {
|
|
2112
1945
|
const outcomeTexts = {
|
|
2113
|
-
positive:
|
|
2114
|
-
negative:
|
|
2115
|
-
mixed:
|
|
1946
|
+
positive: md5`🥳 Code PushUp report has ${md5.bold("improved")}`,
|
|
1947
|
+
negative: md5`😟 Code PushUp report has ${md5.bold("regressed")}`,
|
|
1948
|
+
mixed: md5`🤨 Code PushUp report has both ${md5.bold(
|
|
2116
1949
|
"improvements and regressions"
|
|
2117
1950
|
)}`,
|
|
2118
|
-
unchanged:
|
|
1951
|
+
unchanged: md5`😐 Code PushUp report is ${md5.bold("unchanged")}`
|
|
2119
1952
|
};
|
|
2120
1953
|
const outcome = mergeDiffOutcomes(
|
|
2121
1954
|
changesToDiffOutcomes([
|
|
@@ -2125,143 +1958,127 @@ function formatDiffHeaderSection(diff) {
|
|
|
2125
1958
|
])
|
|
2126
1959
|
);
|
|
2127
1960
|
const styleCommits = (commits) => `compared target commit ${commits.after.hash} with source commit ${commits.before.hash}`;
|
|
2128
|
-
return
|
|
2129
|
-
|
|
2130
|
-
|
|
1961
|
+
return new MarkdownDocument4().heading(HIERARCHY.level_1, "Code PushUp").paragraph(
|
|
1962
|
+
diff.commits ? md5`${outcomeTexts[outcome]} – ${styleCommits(diff.commits)}.` : outcomeTexts[outcome]
|
|
1963
|
+
).paragraph(
|
|
1964
|
+
portalUrl && md5.link(portalUrl, "\u{1F575}\uFE0F See full comparison in Code PushUp portal \u{1F50D}")
|
|
2131
1965
|
);
|
|
2132
1966
|
}
|
|
2133
|
-
function
|
|
1967
|
+
function createDiffCategoriesSection(diff) {
|
|
2134
1968
|
const { changed, unchanged, added } = diff.categories;
|
|
2135
1969
|
const categoriesCount = changed.length + unchanged.length + added.length;
|
|
2136
1970
|
const hasChanges = unchanged.length < categoriesCount;
|
|
2137
1971
|
if (categoriesCount === 0) {
|
|
2138
|
-
return
|
|
1972
|
+
return null;
|
|
2139
1973
|
}
|
|
2140
1974
|
const columns = [
|
|
2141
|
-
{
|
|
2142
|
-
{
|
|
2143
|
-
|
|
2144
|
-
|
|
1975
|
+
{ heading: "\u{1F3F7}\uFE0F Category", alignment: "left" },
|
|
1976
|
+
{
|
|
1977
|
+
heading: hasChanges ? "\u2B50 Previous score" : "\u2B50 Score",
|
|
1978
|
+
alignment: "center"
|
|
1979
|
+
},
|
|
1980
|
+
{ heading: "\u2B50 Current score", alignment: "center" },
|
|
1981
|
+
{ heading: "\u{1F504} Score change", alignment: "center" }
|
|
2145
1982
|
];
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
(row) => hasChanges ? row : { category: row.category, before: row.before }
|
|
2173
|
-
)
|
|
2174
|
-
}),
|
|
2175
|
-
added.length > 0 && section5(italicMd("(\\*) New category."))
|
|
2176
|
-
);
|
|
1983
|
+
const rows = [
|
|
1984
|
+
...sortChanges(changed).map((category) => [
|
|
1985
|
+
formatTitle(category),
|
|
1986
|
+
formatScoreWithColor(category.scores.before, {
|
|
1987
|
+
skipBold: true
|
|
1988
|
+
}),
|
|
1989
|
+
formatScoreWithColor(category.scores.after),
|
|
1990
|
+
formatScoreChange(category.scores.diff)
|
|
1991
|
+
]),
|
|
1992
|
+
...added.map((category) => [
|
|
1993
|
+
formatTitle(category),
|
|
1994
|
+
md5.italic("n/a (\\*)"),
|
|
1995
|
+
formatScoreWithColor(category.score),
|
|
1996
|
+
md5.italic("n/a (\\*)")
|
|
1997
|
+
]),
|
|
1998
|
+
...unchanged.map((category) => [
|
|
1999
|
+
formatTitle(category),
|
|
2000
|
+
formatScoreWithColor(category.score, { skipBold: true }),
|
|
2001
|
+
formatScoreWithColor(category.score),
|
|
2002
|
+
"\u2013"
|
|
2003
|
+
])
|
|
2004
|
+
];
|
|
2005
|
+
return new MarkdownDocument4().heading(HIERARCHY.level_2, "\u{1F3F7}\uFE0F Categories").table(
|
|
2006
|
+
hasChanges ? columns : columns.slice(0, 2),
|
|
2007
|
+
rows.map((row) => hasChanges ? row : row.slice(0, 2))
|
|
2008
|
+
).paragraph(added.length > 0 && md5.italic("(\\*) New category."));
|
|
2177
2009
|
}
|
|
2178
|
-
function
|
|
2010
|
+
function createDiffGroupsSection(diff) {
|
|
2179
2011
|
if (diff.groups.changed.length + diff.groups.unchanged.length === 0) {
|
|
2180
|
-
return
|
|
2181
|
-
}
|
|
2182
|
-
return
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
2186
|
-
|
|
2187
|
-
{
|
|
2188
|
-
{
|
|
2189
|
-
{
|
|
2190
|
-
{
|
|
2012
|
+
return null;
|
|
2013
|
+
}
|
|
2014
|
+
return new MarkdownDocument4().heading(HIERARCHY.level_2, "\u{1F5C3}\uFE0F Groups").$concat(
|
|
2015
|
+
createGroupsOrAuditsDetails(
|
|
2016
|
+
"group",
|
|
2017
|
+
diff.groups,
|
|
2018
|
+
[
|
|
2019
|
+
{ heading: "\u{1F50C} Plugin", alignment: "left" },
|
|
2020
|
+
{ heading: "\u{1F5C3}\uFE0F Group", alignment: "left" },
|
|
2021
|
+
{ heading: "\u2B50 Previous score", alignment: "center" },
|
|
2022
|
+
{ heading: "\u2B50 Current score", alignment: "center" },
|
|
2023
|
+
{ heading: "\u{1F504} Score change", alignment: "center" }
|
|
2191
2024
|
],
|
|
2192
|
-
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2025
|
+
sortChanges(diff.groups.changed).map((group) => [
|
|
2026
|
+
formatTitle(group.plugin),
|
|
2027
|
+
formatTitle(group),
|
|
2028
|
+
formatScoreWithColor(group.scores.before, { skipBold: true }),
|
|
2029
|
+
formatScoreWithColor(group.scores.after),
|
|
2030
|
+
formatScoreChange(group.scores.diff)
|
|
2031
|
+
])
|
|
2032
|
+
)
|
|
2200
2033
|
);
|
|
2201
2034
|
}
|
|
2202
|
-
function
|
|
2203
|
-
return
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
{
|
|
2209
|
-
{
|
|
2210
|
-
{
|
|
2211
|
-
{
|
|
2035
|
+
function createDiffAuditsSection(diff) {
|
|
2036
|
+
return new MarkdownDocument4().heading(HIERARCHY.level_2, "\u{1F6E1}\uFE0F Audits").$concat(
|
|
2037
|
+
createGroupsOrAuditsDetails(
|
|
2038
|
+
"audit",
|
|
2039
|
+
diff.audits,
|
|
2040
|
+
[
|
|
2041
|
+
{ heading: "\u{1F50C} Plugin", alignment: "left" },
|
|
2042
|
+
{ heading: "\u{1F6E1}\uFE0F Audit", alignment: "left" },
|
|
2043
|
+
{ heading: "\u{1F4CF} Previous value", alignment: "center" },
|
|
2044
|
+
{ heading: "\u{1F4CF} Current value", alignment: "center" },
|
|
2045
|
+
{ heading: "\u{1F504} Value change", alignment: "center" }
|
|
2212
2046
|
],
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2047
|
+
sortChanges(diff.audits.changed).map((audit) => [
|
|
2048
|
+
formatTitle(audit.plugin),
|
|
2049
|
+
formatTitle(audit),
|
|
2050
|
+
`${scoreMarker(audit.scores.before, "square")} ${audit.displayValues.before || audit.values.before.toString()}`,
|
|
2051
|
+
md5`${scoreMarker(audit.scores.after, "square")} ${md5.bold(
|
|
2217
2052
|
audit.displayValues.after || audit.values.after.toString()
|
|
2218
2053
|
)}`,
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
})
|
|
2054
|
+
formatValueChange(audit)
|
|
2055
|
+
])
|
|
2056
|
+
)
|
|
2223
2057
|
);
|
|
2224
2058
|
}
|
|
2225
|
-
function
|
|
2226
|
-
|
|
2059
|
+
function createGroupsOrAuditsDetails(token, { changed, unchanged }, ...[columns, rows]) {
|
|
2060
|
+
if (changed.length === 0) {
|
|
2061
|
+
return new MarkdownDocument4().paragraph(
|
|
2062
|
+
summarizeUnchanged(token, { changed, unchanged })
|
|
2063
|
+
);
|
|
2064
|
+
}
|
|
2065
|
+
return new MarkdownDocument4().details(
|
|
2227
2066
|
summarizeDiffOutcomes(changesToDiffOutcomes(changed), token),
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
...tableData,
|
|
2231
|
-
rows: tableData.rows.slice(0, MAX_ROWS)
|
|
2232
|
-
// use never to avoid typing problem
|
|
2233
|
-
}),
|
|
2234
|
-
changed.length > MAX_ROWS && italicMd(
|
|
2067
|
+
md5`${md5.table(columns, rows.slice(0, MAX_ROWS))}${changed.length > MAX_ROWS ? md5.paragraph(
|
|
2068
|
+
md5.italic(
|
|
2235
2069
|
`Only the ${MAX_ROWS} most affected ${pluralize(
|
|
2236
2070
|
token
|
|
2237
2071
|
)} are listed above for brevity.`
|
|
2238
|
-
)
|
|
2239
|
-
|
|
2240
|
-
)
|
|
2072
|
+
)
|
|
2073
|
+
) : ""}${unchanged.length > 0 ? md5.paragraph(summarizeUnchanged(token, { changed, unchanged })) : ""}`
|
|
2241
2074
|
);
|
|
2242
2075
|
}
|
|
2243
|
-
function formatScoreChange(diff) {
|
|
2244
|
-
const marker = getDiffMarker(diff);
|
|
2245
|
-
const text = formatDiffNumber(Math.round(diff * 1e3) / 10);
|
|
2246
|
-
return colorByScoreDiff(`${marker} ${text}`, diff);
|
|
2247
|
-
}
|
|
2248
|
-
function formatValueChange({
|
|
2249
|
-
values,
|
|
2250
|
-
scores
|
|
2251
|
-
}) {
|
|
2252
|
-
const marker = getDiffMarker(values.diff);
|
|
2253
|
-
const percentage = values.before === 0 ? values.diff > 0 ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY : Math.round(100 * values.diff / values.before);
|
|
2254
|
-
const text = `${formatDiffNumber(percentage)}\u2009%`;
|
|
2255
|
-
return colorByScoreDiff(`${marker} ${text}`, scores.diff);
|
|
2256
|
-
}
|
|
2257
2076
|
function summarizeUnchanged(token, { changed, unchanged }) {
|
|
2258
|
-
return
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
].join(" ")
|
|
2264
|
-
);
|
|
2077
|
+
return [
|
|
2078
|
+
changed.length > 0 ? pluralizeToken(`other ${token}`, unchanged.length) : `All of ${pluralizeToken(token, unchanged.length)}`,
|
|
2079
|
+
unchanged.length === 1 ? "is" : "are",
|
|
2080
|
+
"unchanged."
|
|
2081
|
+
].join(" ");
|
|
2265
2082
|
}
|
|
2266
2083
|
function summarizeDiffOutcomes(outcomes, token) {
|
|
2267
2084
|
return objectToEntries(countDiffOutcomes(outcomes)).filter(
|
|
@@ -2286,7 +2103,7 @@ function formatTitle({
|
|
|
2286
2103
|
docsUrl
|
|
2287
2104
|
}) {
|
|
2288
2105
|
if (docsUrl) {
|
|
2289
|
-
return
|
|
2106
|
+
return md5.link(docsUrl, title);
|
|
2290
2107
|
}
|
|
2291
2108
|
return title;
|
|
2292
2109
|
}
|
|
@@ -2330,8 +2147,22 @@ function countDiffOutcomes(outcomes) {
|
|
|
2330
2147
|
};
|
|
2331
2148
|
}
|
|
2332
2149
|
|
|
2150
|
+
// packages/utils/src/lib/reports/load-report.ts
|
|
2151
|
+
import { join as join2 } from "node:path";
|
|
2152
|
+
async function loadReport(options2) {
|
|
2153
|
+
const { outputDir, filename, format } = options2;
|
|
2154
|
+
await ensureDirectoryExists(outputDir);
|
|
2155
|
+
const filePath = join2(outputDir, `${filename}.${format}`);
|
|
2156
|
+
if (format === "json") {
|
|
2157
|
+
const content = await readJsonFile(filePath);
|
|
2158
|
+
return reportSchema.parse(content);
|
|
2159
|
+
}
|
|
2160
|
+
const text = await readTextFile(filePath);
|
|
2161
|
+
return text;
|
|
2162
|
+
}
|
|
2163
|
+
|
|
2333
2164
|
// packages/utils/src/lib/reports/log-stdout-summary.ts
|
|
2334
|
-
import
|
|
2165
|
+
import { bold as bold4, cyan, cyanBright, green as green2, red } from "ansis";
|
|
2335
2166
|
function log(msg = "") {
|
|
2336
2167
|
ui().logger.log(msg);
|
|
2337
2168
|
}
|
|
@@ -2348,14 +2179,14 @@ function logStdoutSummary(report) {
|
|
|
2348
2179
|
}
|
|
2349
2180
|
function reportToHeaderSection(report) {
|
|
2350
2181
|
const { packageName, version: version2 } = report;
|
|
2351
|
-
return `${
|
|
2182
|
+
return `${bold4(REPORT_HEADLINE_TEXT)} - ${packageName}@${version2}`;
|
|
2352
2183
|
}
|
|
2353
2184
|
function logPlugins(report) {
|
|
2354
2185
|
const { plugins } = report;
|
|
2355
2186
|
plugins.forEach((plugin) => {
|
|
2356
2187
|
const { title, audits } = plugin;
|
|
2357
2188
|
log();
|
|
2358
|
-
log(
|
|
2189
|
+
log(bold4.magentaBright(`${title} audits`));
|
|
2359
2190
|
log();
|
|
2360
2191
|
audits.forEach((audit) => {
|
|
2361
2192
|
ui().row([
|
|
@@ -2370,7 +2201,7 @@ function logPlugins(report) {
|
|
|
2370
2201
|
padding: [0, 3, 0, 0]
|
|
2371
2202
|
},
|
|
2372
2203
|
{
|
|
2373
|
-
text:
|
|
2204
|
+
text: cyanBright(audit.displayValue || `${audit.value}`),
|
|
2374
2205
|
width: 10,
|
|
2375
2206
|
padding: [0, 0, 0, 0]
|
|
2376
2207
|
}
|
|
@@ -2381,42 +2212,38 @@ function logPlugins(report) {
|
|
|
2381
2212
|
}
|
|
2382
2213
|
function logCategories({ categories, plugins }) {
|
|
2383
2214
|
const hAlign = (idx) => idx === 0 ? "left" : "right";
|
|
2384
|
-
const rows = categories.map(({ title, score, refs }) => [
|
|
2215
|
+
const rows = categories.map(({ title, score, refs, isBinary }) => [
|
|
2385
2216
|
title,
|
|
2386
|
-
applyScoreColor({ score })
|
|
2217
|
+
`${binaryIconPrefix(score, isBinary)}${applyScoreColor({ score })}`,
|
|
2387
2218
|
countCategoryAudits(refs, plugins)
|
|
2388
2219
|
]);
|
|
2389
|
-
const
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
content:
|
|
2220
|
+
const table2 = ui().table();
|
|
2221
|
+
table2.columnWidths([TERMINAL_WIDTH - 9 - 10 - 4, 9, 10]);
|
|
2222
|
+
table2.head(
|
|
2223
|
+
REPORT_RAW_OVERVIEW_TABLE_HEADERS.map((heading, idx) => ({
|
|
2224
|
+
content: cyan(heading),
|
|
2394
2225
|
hAlign: hAlign(idx)
|
|
2395
2226
|
}))
|
|
2396
2227
|
);
|
|
2397
2228
|
rows.forEach(
|
|
2398
|
-
(row) =>
|
|
2229
|
+
(row) => table2.row(
|
|
2399
2230
|
row.map((content, idx) => ({
|
|
2400
2231
|
content: content.toString(),
|
|
2401
2232
|
hAlign: hAlign(idx)
|
|
2402
2233
|
}))
|
|
2403
2234
|
)
|
|
2404
2235
|
);
|
|
2405
|
-
log(
|
|
2236
|
+
log(bold4.magentaBright("Categories"));
|
|
2406
2237
|
log();
|
|
2407
|
-
|
|
2238
|
+
table2.render();
|
|
2408
2239
|
log();
|
|
2409
2240
|
}
|
|
2410
|
-
function
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
}
|
|
2416
|
-
if (score >= SCORE_COLOR_RANGE.YELLOW_MIN) {
|
|
2417
|
-
return style.yellow(formattedScore);
|
|
2418
|
-
}
|
|
2419
|
-
return style.red(formattedScore);
|
|
2241
|
+
function binaryIconPrefix(score, isBinary) {
|
|
2242
|
+
return targetScoreIcon(score, isBinary ? 1 : void 0, {
|
|
2243
|
+
passIcon: bold4(green2("\u2713")),
|
|
2244
|
+
failIcon: bold4(red("\u2717")),
|
|
2245
|
+
postfix: " "
|
|
2246
|
+
});
|
|
2420
2247
|
}
|
|
2421
2248
|
|
|
2422
2249
|
// packages/utils/src/lib/reports/scoring.ts
|
|
@@ -2506,56 +2333,6 @@ function parseScoringParameters(refs, scoreFn) {
|
|
|
2506
2333
|
return scoredRefs;
|
|
2507
2334
|
}
|
|
2508
2335
|
|
|
2509
|
-
// packages/utils/src/lib/reports/sorting.ts
|
|
2510
|
-
function sortReport(report) {
|
|
2511
|
-
const { categories, plugins } = report;
|
|
2512
|
-
const sortedCategories = categories.map((category) => {
|
|
2513
|
-
const { audits, groups: groups2 } = category.refs.reduce(
|
|
2514
|
-
(acc, ref) => ({
|
|
2515
|
-
...acc,
|
|
2516
|
-
...ref.type === "group" ? {
|
|
2517
|
-
groups: [...acc.groups, getSortableGroupByRef(ref, plugins)]
|
|
2518
|
-
} : {
|
|
2519
|
-
audits: [...acc.audits, getSortableAuditByRef(ref, plugins)]
|
|
2520
|
-
}
|
|
2521
|
-
}),
|
|
2522
|
-
{ groups: [], audits: [] }
|
|
2523
|
-
);
|
|
2524
|
-
const sortedAuditsAndGroups = [...audits, ...groups2].sort(
|
|
2525
|
-
compareCategoryAuditsAndGroups
|
|
2526
|
-
);
|
|
2527
|
-
const sortedRefs = [...category.refs].sort((a, b) => {
|
|
2528
|
-
const aIndex = sortedAuditsAndGroups.findIndex(
|
|
2529
|
-
(ref) => ref.slug === a.slug && ref.plugin === a.plugin
|
|
2530
|
-
);
|
|
2531
|
-
const bIndex = sortedAuditsAndGroups.findIndex(
|
|
2532
|
-
(ref) => ref.slug === b.slug && ref.plugin === b.plugin
|
|
2533
|
-
);
|
|
2534
|
-
return aIndex - bIndex;
|
|
2535
|
-
});
|
|
2536
|
-
return { ...category, refs: sortedRefs };
|
|
2537
|
-
});
|
|
2538
|
-
return {
|
|
2539
|
-
...report,
|
|
2540
|
-
categories: sortedCategories,
|
|
2541
|
-
plugins: sortPlugins(plugins)
|
|
2542
|
-
};
|
|
2543
|
-
}
|
|
2544
|
-
function sortPlugins(plugins) {
|
|
2545
|
-
return plugins.map((plugin) => ({
|
|
2546
|
-
...plugin,
|
|
2547
|
-
audits: [...plugin.audits].sort(compareAudits).map(
|
|
2548
|
-
(audit) => audit.details?.issues ? {
|
|
2549
|
-
...audit,
|
|
2550
|
-
details: {
|
|
2551
|
-
...audit.details,
|
|
2552
|
-
issues: [...audit.details.issues].sort(compareIssues)
|
|
2553
|
-
}
|
|
2554
|
-
} : audit
|
|
2555
|
-
)
|
|
2556
|
-
}));
|
|
2557
|
-
}
|
|
2558
|
-
|
|
2559
2336
|
// packages/utils/src/lib/verbose-utils.ts
|
|
2560
2337
|
function getLogVerbose(verbose = false) {
|
|
2561
2338
|
return (msg) => {
|
|
@@ -2578,10 +2355,10 @@ var verboseUtils = (verbose = false) => ({
|
|
|
2578
2355
|
|
|
2579
2356
|
// packages/core/package.json
|
|
2580
2357
|
var name = "@code-pushup/core";
|
|
2581
|
-
var version = "0.
|
|
2358
|
+
var version = "0.49.0";
|
|
2582
2359
|
|
|
2583
2360
|
// packages/core/src/lib/implementation/execute-plugin.ts
|
|
2584
|
-
import
|
|
2361
|
+
import { bold as bold5 } from "ansis";
|
|
2585
2362
|
|
|
2586
2363
|
// packages/core/src/lib/normalize.ts
|
|
2587
2364
|
function normalizeIssue(issue, gitRoot) {
|
|
@@ -2644,7 +2421,7 @@ async function executeRunnerFunction(runner, onProgress) {
|
|
|
2644
2421
|
var PluginOutputMissingAuditError = class extends Error {
|
|
2645
2422
|
constructor(auditSlug) {
|
|
2646
2423
|
super(
|
|
2647
|
-
`Audit metadata not present in plugin config. Missing slug: ${
|
|
2424
|
+
`Audit metadata not present in plugin config. Missing slug: ${bold5(
|
|
2648
2425
|
auditSlug
|
|
2649
2426
|
)}`
|
|
2650
2427
|
);
|
|
@@ -2686,7 +2463,7 @@ async function executePlugin(pluginConfig, onProgress) {
|
|
|
2686
2463
|
};
|
|
2687
2464
|
}
|
|
2688
2465
|
var wrapProgress = async (pluginCfg, steps, progressBar) => {
|
|
2689
|
-
progressBar?.updateTitle(`Executing ${
|
|
2466
|
+
progressBar?.updateTitle(`Executing ${bold5(pluginCfg.title)}`);
|
|
2690
2467
|
try {
|
|
2691
2468
|
const pluginReport = await executePlugin(pluginCfg);
|
|
2692
2469
|
progressBar?.incrementInSteps(steps);
|
|
@@ -2694,7 +2471,7 @@ var wrapProgress = async (pluginCfg, steps, progressBar) => {
|
|
|
2694
2471
|
} catch (error) {
|
|
2695
2472
|
progressBar?.incrementInSteps(steps);
|
|
2696
2473
|
throw new Error(
|
|
2697
|
-
error instanceof Error ? `- Plugin ${
|
|
2474
|
+
error instanceof Error ? `- Plugin ${bold5(pluginCfg.title)} (${bold5(
|
|
2698
2475
|
pluginCfg.slug
|
|
2699
2476
|
)}) produced the following error:
|
|
2700
2477
|
- ${error.message}` : String(error)
|
|
@@ -2834,6 +2611,10 @@ async function collectAndPersistReports(options2) {
|
|
|
2834
2611
|
// packages/core/src/lib/compare.ts
|
|
2835
2612
|
import { writeFile as writeFile2 } from "node:fs/promises";
|
|
2836
2613
|
import { join as join5 } from "node:path";
|
|
2614
|
+
import {
|
|
2615
|
+
PortalOperationError,
|
|
2616
|
+
getPortalComparisonLink
|
|
2617
|
+
} from "@code-pushup/portal-client";
|
|
2837
2618
|
|
|
2838
2619
|
// packages/core/src/lib/implementation/compare-scorables.ts
|
|
2839
2620
|
function compareCategories(reports) {
|
|
@@ -2970,7 +2751,7 @@ function selectMeta(meta) {
|
|
|
2970
2751
|
}
|
|
2971
2752
|
|
|
2972
2753
|
// packages/core/src/lib/compare.ts
|
|
2973
|
-
async function compareReportFiles(inputPaths, persistConfig) {
|
|
2754
|
+
async function compareReportFiles(inputPaths, persistConfig, uploadConfig) {
|
|
2974
2755
|
const { outputDir, filename, format } = persistConfig;
|
|
2975
2756
|
const [reportBefore, reportAfter] = await Promise.all([
|
|
2976
2757
|
readJsonFile(inputPaths.before),
|
|
@@ -2981,10 +2762,11 @@ async function compareReportFiles(inputPaths, persistConfig) {
|
|
|
2981
2762
|
after: reportSchema.parse(reportAfter)
|
|
2982
2763
|
};
|
|
2983
2764
|
const reportsDiff = compareReports(reports);
|
|
2765
|
+
const portalUrl = uploadConfig && reportsDiff.commits && format.includes("md") ? await fetchPortalComparisonLink(uploadConfig, reportsDiff.commits) : void 0;
|
|
2984
2766
|
return Promise.all(
|
|
2985
2767
|
format.map(async (fmt) => {
|
|
2986
2768
|
const outputPath = join5(outputDir, `${filename}-diff.${fmt}`);
|
|
2987
|
-
const content = reportsDiffToFileContent(reportsDiff, fmt);
|
|
2769
|
+
const content = reportsDiffToFileContent(reportsDiff, fmt, portalUrl);
|
|
2988
2770
|
await ensureDirectoryExists(outputDir);
|
|
2989
2771
|
await writeFile2(outputPath, content);
|
|
2990
2772
|
return outputPath;
|
|
@@ -3014,12 +2796,35 @@ function compareReports(reports) {
|
|
|
3014
2796
|
duration
|
|
3015
2797
|
};
|
|
3016
2798
|
}
|
|
3017
|
-
function reportsDiffToFileContent(reportsDiff, format) {
|
|
2799
|
+
function reportsDiffToFileContent(reportsDiff, format, portalUrl) {
|
|
3018
2800
|
switch (format) {
|
|
3019
2801
|
case "json":
|
|
3020
2802
|
return JSON.stringify(reportsDiff, null, 2);
|
|
3021
2803
|
case "md":
|
|
3022
|
-
return generateMdReportsDiff(reportsDiff);
|
|
2804
|
+
return generateMdReportsDiff(reportsDiff, portalUrl ?? void 0);
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
async function fetchPortalComparisonLink(uploadConfig, commits) {
|
|
2808
|
+
const { server, apiKey, organization, project } = uploadConfig;
|
|
2809
|
+
try {
|
|
2810
|
+
return await getPortalComparisonLink({
|
|
2811
|
+
server,
|
|
2812
|
+
apiKey,
|
|
2813
|
+
parameters: {
|
|
2814
|
+
organization,
|
|
2815
|
+
project,
|
|
2816
|
+
before: commits.before.hash,
|
|
2817
|
+
after: commits.after.hash
|
|
2818
|
+
}
|
|
2819
|
+
});
|
|
2820
|
+
} catch (error) {
|
|
2821
|
+
if (error instanceof PortalOperationError) {
|
|
2822
|
+
ui().logger.warning(
|
|
2823
|
+
`Failed to fetch portal comparison link - ${error.message}`
|
|
2824
|
+
);
|
|
2825
|
+
return void 0;
|
|
2826
|
+
}
|
|
2827
|
+
throw error;
|
|
3023
2828
|
}
|
|
3024
2829
|
}
|
|
3025
2830
|
|
|
@@ -3077,9 +2882,9 @@ function auditToGQL(audit) {
|
|
|
3077
2882
|
score,
|
|
3078
2883
|
value,
|
|
3079
2884
|
displayValue: formattedValue,
|
|
3080
|
-
details:
|
|
2885
|
+
details: details2
|
|
3081
2886
|
} = audit;
|
|
3082
|
-
const { issues, table:
|
|
2887
|
+
const { issues, table: table2 } = details2 ?? {};
|
|
3083
2888
|
return {
|
|
3084
2889
|
slug,
|
|
3085
2890
|
title,
|
|
@@ -3088,10 +2893,10 @@ function auditToGQL(audit) {
|
|
|
3088
2893
|
score,
|
|
3089
2894
|
value,
|
|
3090
2895
|
formattedValue,
|
|
3091
|
-
...
|
|
2896
|
+
...details2 && {
|
|
3092
2897
|
details: {
|
|
3093
2898
|
...issues && { issues: issues.map(issueToGQL) },
|
|
3094
|
-
...
|
|
2899
|
+
...table2 && { tables: [tableToGQL(table2)] }
|
|
3095
2900
|
}
|
|
3096
2901
|
}
|
|
3097
2902
|
};
|
|
@@ -3110,11 +2915,11 @@ function issueToGQL(issue) {
|
|
|
3110
2915
|
}
|
|
3111
2916
|
};
|
|
3112
2917
|
}
|
|
3113
|
-
function tableToGQL(
|
|
2918
|
+
function tableToGQL(table2) {
|
|
3114
2919
|
return {
|
|
3115
|
-
...
|
|
3116
|
-
...
|
|
3117
|
-
columns:
|
|
2920
|
+
...table2.title && { title: table2.title },
|
|
2921
|
+
...table2.columns?.length && {
|
|
2922
|
+
columns: table2.columns.map(
|
|
3118
2923
|
(column) => typeof column === "string" ? { alignment: tableAlignmentToGQL(column) } : {
|
|
3119
2924
|
key: column.key,
|
|
3120
2925
|
label: column.label,
|
|
@@ -3122,7 +2927,7 @@ function tableToGQL(table5) {
|
|
|
3122
2927
|
}
|
|
3123
2928
|
)
|
|
3124
2929
|
},
|
|
3125
|
-
rows:
|
|
2930
|
+
rows: table2.rows.map(
|
|
3126
2931
|
(row) => Array.isArray(row) ? row.map((content) => ({ content: content?.toString() ?? "" })) : Object.entries(row).map(([key, content]) => ({
|
|
3127
2932
|
key,
|
|
3128
2933
|
content: content?.toString() ?? ""
|
|
@@ -3274,10 +3079,10 @@ var CLI_NAME = "Code PushUp CLI";
|
|
|
3274
3079
|
var CLI_SCRIPT_NAME = "code-pushup";
|
|
3275
3080
|
|
|
3276
3081
|
// packages/cli/src/lib/implementation/logging.ts
|
|
3277
|
-
import
|
|
3082
|
+
import { bold as bold6, gray as gray3 } from "ansis";
|
|
3278
3083
|
function renderConfigureCategoriesHint() {
|
|
3279
3084
|
ui().logger.info(
|
|
3280
|
-
|
|
3085
|
+
gray3(
|
|
3281
3086
|
`\u{1F4A1} Configure categories to see the scores in an overview table. See: ${link(
|
|
3282
3087
|
"https://github.com/code-pushup/cli/blob/main/packages/cli/README.md"
|
|
3283
3088
|
)}`
|
|
@@ -3292,8 +3097,8 @@ function collectSuccessfulLog() {
|
|
|
3292
3097
|
ui().logger.success("Collecting report successful!");
|
|
3293
3098
|
}
|
|
3294
3099
|
function renderIntegratePortalHint() {
|
|
3295
|
-
ui().sticker().add(
|
|
3296
|
-
`${
|
|
3100
|
+
ui().sticker().add(bold6.gray("\u{1F4A1} Integrate the portal")).add("").add(
|
|
3101
|
+
`${gray3("\u276F")} Upload a report to the server - ${gray3(
|
|
3297
3102
|
"npx code-pushup upload"
|
|
3298
3103
|
)}`
|
|
3299
3104
|
).add(
|
|
@@ -3301,11 +3106,11 @@ function renderIntegratePortalHint() {
|
|
|
3301
3106
|
"https://github.com/code-pushup/cli/tree/main/packages/cli#upload-command"
|
|
3302
3107
|
)}`
|
|
3303
3108
|
).add(
|
|
3304
|
-
`${
|
|
3109
|
+
`${gray3("\u276F")} ${gray3("Portal Integration")} - ${link(
|
|
3305
3110
|
"https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#portal-integration"
|
|
3306
3111
|
)}`
|
|
3307
3112
|
).add(
|
|
3308
|
-
`${
|
|
3113
|
+
`${gray3("\u276F")} ${gray3("Upload Command")} - ${link(
|
|
3309
3114
|
"https://github.com/code-pushup/cli/blob/main/packages/cli/README.md#portal-integration"
|
|
3310
3115
|
)}`
|
|
3311
3116
|
).render();
|
|
@@ -3318,8 +3123,8 @@ function yargsAutorunCommandObject() {
|
|
|
3318
3123
|
command: command2,
|
|
3319
3124
|
describe: "Shortcut for running collect followed by upload",
|
|
3320
3125
|
handler: async (args) => {
|
|
3321
|
-
ui().logger.log(
|
|
3322
|
-
ui().logger.info(
|
|
3126
|
+
ui().logger.log(bold7(CLI_NAME));
|
|
3127
|
+
ui().logger.info(gray4(`Run ${command2}...`));
|
|
3323
3128
|
const options2 = args;
|
|
3324
3129
|
const optionsWithFormat = {
|
|
3325
3130
|
...options2,
|
|
@@ -3347,7 +3152,7 @@ function yargsAutorunCommandObject() {
|
|
|
3347
3152
|
}
|
|
3348
3153
|
|
|
3349
3154
|
// packages/cli/src/lib/collect/collect-command.ts
|
|
3350
|
-
import
|
|
3155
|
+
import { bold as bold8, gray as gray5 } from "ansis";
|
|
3351
3156
|
function yargsCollectCommandObject() {
|
|
3352
3157
|
const command2 = "collect";
|
|
3353
3158
|
return {
|
|
@@ -3355,8 +3160,8 @@ function yargsCollectCommandObject() {
|
|
|
3355
3160
|
describe: "Run Plugins and collect results",
|
|
3356
3161
|
handler: async (args) => {
|
|
3357
3162
|
const options2 = args;
|
|
3358
|
-
ui().logger.log(
|
|
3359
|
-
ui().logger.info(
|
|
3163
|
+
ui().logger.log(bold8(CLI_NAME));
|
|
3164
|
+
ui().logger.info(gray5(`Run ${command2}...`));
|
|
3360
3165
|
await collectAndPersistReports(options2);
|
|
3361
3166
|
collectSuccessfulLog();
|
|
3362
3167
|
if (options2.categories.length === 0) {
|
|
@@ -3370,8 +3175,8 @@ function yargsCollectCommandObject() {
|
|
|
3370
3175
|
};
|
|
3371
3176
|
}
|
|
3372
3177
|
function renderUploadAutorunHint() {
|
|
3373
|
-
ui().sticker().add(
|
|
3374
|
-
`${
|
|
3178
|
+
ui().sticker().add(bold8.gray("\u{1F4A1} Visualize your reports")).add("").add(
|
|
3179
|
+
`${gray5("\u276F")} npx code-pushup upload - ${gray5(
|
|
3375
3180
|
"Run upload to upload the created report to the server"
|
|
3376
3181
|
)}`
|
|
3377
3182
|
).add(
|
|
@@ -3379,9 +3184,7 @@ function renderUploadAutorunHint() {
|
|
|
3379
3184
|
"https://github.com/code-pushup/cli/tree/main/packages/cli#upload-command"
|
|
3380
3185
|
)}`
|
|
3381
3186
|
).add(
|
|
3382
|
-
`${
|
|
3383
|
-
"Run collect & upload"
|
|
3384
|
-
)}`
|
|
3187
|
+
`${gray5("\u276F")} npx code-pushup autorun - ${gray5("Run collect & upload")}`
|
|
3385
3188
|
).add(
|
|
3386
3189
|
` ${link(
|
|
3387
3190
|
"https://github.com/code-pushup/cli/tree/main/packages/cli#autorun-command"
|
|
@@ -3390,7 +3193,7 @@ function renderUploadAutorunHint() {
|
|
|
3390
3193
|
}
|
|
3391
3194
|
|
|
3392
3195
|
// packages/cli/src/lib/compare/compare-command.ts
|
|
3393
|
-
import
|
|
3196
|
+
import { bold as bold9, gray as gray6 } from "ansis";
|
|
3394
3197
|
|
|
3395
3198
|
// packages/cli/src/lib/implementation/compare.options.ts
|
|
3396
3199
|
function yargsCompareOptionsDefinition() {
|
|
@@ -3416,20 +3219,24 @@ function yargsCompareCommandObject() {
|
|
|
3416
3219
|
describe: "Compare 2 report files and create a diff file",
|
|
3417
3220
|
builder: yargsCompareOptionsDefinition(),
|
|
3418
3221
|
handler: async (args) => {
|
|
3419
|
-
ui().logger.log(
|
|
3420
|
-
ui().logger.info(
|
|
3222
|
+
ui().logger.log(bold9(CLI_NAME));
|
|
3223
|
+
ui().logger.info(gray6(`Run ${command2}...`));
|
|
3421
3224
|
const options2 = args;
|
|
3422
|
-
const { before, after, persist } = options2;
|
|
3423
|
-
const outputPaths = await compareReportFiles(
|
|
3225
|
+
const { before, after, persist, upload: upload2 } = options2;
|
|
3226
|
+
const outputPaths = await compareReportFiles(
|
|
3227
|
+
{ before, after },
|
|
3228
|
+
persist,
|
|
3229
|
+
upload2
|
|
3230
|
+
);
|
|
3424
3231
|
ui().logger.info(
|
|
3425
|
-
`Reports diff written to ${outputPaths.map((path) =>
|
|
3232
|
+
`Reports diff written to ${outputPaths.map((path) => bold9(path)).join(" and ")}`
|
|
3426
3233
|
);
|
|
3427
3234
|
}
|
|
3428
3235
|
};
|
|
3429
3236
|
}
|
|
3430
3237
|
|
|
3431
3238
|
// packages/cli/src/lib/history/history-command.ts
|
|
3432
|
-
import
|
|
3239
|
+
import { bold as bold10, gray as gray7 } from "ansis";
|
|
3433
3240
|
|
|
3434
3241
|
// packages/cli/src/lib/implementation/global.utils.ts
|
|
3435
3242
|
function filterKebabCaseKeys(obj) {
|
|
@@ -3555,8 +3362,8 @@ async function normalizeHashOptions(processArgs) {
|
|
|
3555
3362
|
// packages/cli/src/lib/history/history-command.ts
|
|
3556
3363
|
var command = "history";
|
|
3557
3364
|
async function handler(args) {
|
|
3558
|
-
ui().logger.info(
|
|
3559
|
-
ui().logger.info(
|
|
3365
|
+
ui().logger.info(bold10(CLI_NAME));
|
|
3366
|
+
ui().logger.info(gray7(`Run ${command}`));
|
|
3560
3367
|
const currentBranch = await getCurrentBranchOrTag();
|
|
3561
3368
|
const { targetBranch: rawTargetBranch, ...opt } = args;
|
|
3562
3369
|
const {
|
|
@@ -3620,15 +3427,15 @@ function yargsConfigCommandObject() {
|
|
|
3620
3427
|
}
|
|
3621
3428
|
|
|
3622
3429
|
// packages/cli/src/lib/upload/upload-command.ts
|
|
3623
|
-
import
|
|
3430
|
+
import { bold as bold11, gray as gray8 } from "ansis";
|
|
3624
3431
|
function yargsUploadCommandObject() {
|
|
3625
3432
|
const command2 = "upload";
|
|
3626
3433
|
return {
|
|
3627
3434
|
command: command2,
|
|
3628
3435
|
describe: "Upload report results to the portal",
|
|
3629
3436
|
handler: async (args) => {
|
|
3630
|
-
ui().logger.log(
|
|
3631
|
-
ui().logger.info(
|
|
3437
|
+
ui().logger.log(bold11(CLI_NAME));
|
|
3438
|
+
ui().logger.info(gray8(`Run ${command2}...`));
|
|
3632
3439
|
const options2 = args;
|
|
3633
3440
|
if (options2.upload == null) {
|
|
3634
3441
|
renderIntegratePortalHint();
|
|
@@ -3679,7 +3486,9 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
3679
3486
|
persist: {
|
|
3680
3487
|
outputDir: cliPersist?.outputDir ?? rcPersist?.outputDir ?? DEFAULT_PERSIST_OUTPUT_DIR,
|
|
3681
3488
|
filename: cliPersist?.filename ?? rcPersist?.filename ?? DEFAULT_PERSIST_FILENAME,
|
|
3682
|
-
format:
|
|
3489
|
+
format: normalizeFormats(
|
|
3490
|
+
cliPersist?.format ?? rcPersist?.format ?? DEFAULT_PERSIST_FORMAT
|
|
3491
|
+
)
|
|
3683
3492
|
},
|
|
3684
3493
|
...upload2 != null && { upload: upload2 },
|
|
3685
3494
|
categories: rcCategories ?? [],
|
|
@@ -3687,9 +3496,10 @@ async function coreConfigMiddleware(processArgs) {
|
|
|
3687
3496
|
...remainingCliOptions
|
|
3688
3497
|
};
|
|
3689
3498
|
}
|
|
3499
|
+
var normalizeFormats = (formats) => (formats ?? []).flatMap((format) => format.split(","));
|
|
3690
3500
|
|
|
3691
3501
|
// packages/cli/src/lib/implementation/validate-plugin-filter-options.utils.ts
|
|
3692
|
-
import
|
|
3502
|
+
import { yellow } from "ansis";
|
|
3693
3503
|
function validatePluginFilterOption(filterOption, {
|
|
3694
3504
|
plugins,
|
|
3695
3505
|
categories
|
|
@@ -3705,7 +3515,7 @@ function validatePluginFilterOption(filterOption, {
|
|
|
3705
3515
|
const filterFunction = (plugin) => isSkipOption ? pluginsToFilterSet.has(plugin) : !pluginsToFilterSet.has(plugin);
|
|
3706
3516
|
if (missingPlugins.length > 0 && verbose) {
|
|
3707
3517
|
ui().logger.info(
|
|
3708
|
-
`${
|
|
3518
|
+
`${yellow(
|
|
3709
3519
|
"\u26A0"
|
|
3710
3520
|
)} The --${filterOption} argument references plugins with "${missingPlugins.join(
|
|
3711
3521
|
'", "'
|
|
@@ -3888,7 +3698,7 @@ var groups = {
|
|
|
3888
3698
|
};
|
|
3889
3699
|
|
|
3890
3700
|
// packages/cli/src/lib/yargs-cli.ts
|
|
3891
|
-
import
|
|
3701
|
+
import { bold as bold12 } from "ansis";
|
|
3892
3702
|
import yargs from "yargs";
|
|
3893
3703
|
function yargsCli(argv, cfg) {
|
|
3894
3704
|
const { usageMessage, scriptName, noExitProcess } = cfg;
|
|
@@ -3908,7 +3718,7 @@ function yargsCli(argv, cfg) {
|
|
|
3908
3718
|
(config) => Array.isArray(config) ? config.at(-1) : config
|
|
3909
3719
|
).options(options2).wrap(TERMINAL_WIDTH);
|
|
3910
3720
|
if (usageMessage) {
|
|
3911
|
-
cli2.usage(
|
|
3721
|
+
cli2.usage(bold12(usageMessage));
|
|
3912
3722
|
}
|
|
3913
3723
|
if (scriptName) {
|
|
3914
3724
|
cli2.scriptName(scriptName);
|
|
@@ -3942,7 +3752,7 @@ function yargsCli(argv, cfg) {
|
|
|
3942
3752
|
function validatePersistFormat(persist) {
|
|
3943
3753
|
try {
|
|
3944
3754
|
if (persist.format != null) {
|
|
3945
|
-
persist.format.forEach((format) => formatSchema.parse(format));
|
|
3755
|
+
persist.format.flatMap((format) => format.split(",")).forEach((format) => formatSchema.parse(format));
|
|
3946
3756
|
}
|
|
3947
3757
|
return true;
|
|
3948
3758
|
} catch {
|